$TITLE(BIOS FOR THE IBM PERSONAL COMPUTER XT)

;----------------------------------------------------------------
;	THE  BIOS ROUTINES ARE MEANT TO BE ACCESSED THROUGH	:
;	SOFTWARE INTERRUPTS ONLY.  ANY ADDRESSES PRESENT IN	:
;	THE LISTINGS  ARE INCLUDED  ONLY FOR  COMPLETENESS,	:
;	NOT FOR  REFERENCE.   APPLICATIONS WHICH  REFERENCE	:
;	ABSOLUTE   ADDRESSES   WITHIN	THE   CODE  SEGMENT	:
;	VIOLATE THE STRUCTURE AND DESIGN OF BIOS.		:
;----------------------------------------------------------------

;----------------------------------------
;		EQUATES 		:
;----------------------------------------
PORT_A		EQU	60H		; 8255 PORT A ADDR
PORT_B		EQU	61H		; 8255 PORT B ADDR
PORT_C		EQU	62H		; 8255 PORT C ADDR
CMD_PORT	EQU	63H
INTA00		EQU	20H		; 8259 PORT
INTA01		EQU	21H		; 8259 PORT
EOI		EQU	20H
TIMER		EQU	40H
TIM_CTL 	EQU	43H		; 8253 TIMER CONTROL PORT ADDR
TIMER0		EQU	40H		; 8253 TIMER/CNTER 0 PORT ADDR
TMINT		EQU	01		; TIMER 0 INTR RECVD MASK
DMA08		EQU	08		; DMA STATUS REG PORT ADDR
DMA		EQU	00		; DMA CHANNEL 0 ADDRESS REG PORT ADDR
MAX_PERIOD	EQU	540H
MIN_PERIOD	EQU	410H
KBD_IN		EQU	60H		; KEYBOARD DATA IN ADDR PORT
KBDINT		EQU	02		; KEYBOARD INTR MASK
KB_DATA 	EQU	60H		; KEYBOARD SCAN CODE PORT
KB_CTL		EQU	61H		; CONTROL BITS FOR KEYBOARD SENSE DATA

;----------------------------------------
;	 8088 INTERRUPT LOCATIONS	:
;----------------------------------------

ABS0	SEGMENT AT 0
STG_LOC0	LABEL	BYTE
	ORG	2*4
NMI_PTR 	LABEL	WORD
	ORG	5*4
INT5_PTR	LABEL	WORD
	ORG	8*4
INT_ADDR	LABEL	WORD
INT_PTR 	LABEL	DWORD
	ORG	10H*4
VIDEO_INT	LABEL	WORD
	ORG	1DH*4
PARM_PTR	LABEL	DWORD		; POINTER TO VIDEO PARMS
	ORG	18H*4
BASIC_PTR	LABEL	WORD		; ENTRY POINT FOR CASSETTE BASIC
	ORG	01EH*4			; INTERRUPT 1EH
DISK_POINTER	LABEL	DWORD
	ORG	01FH*4			; LOCATION OF POINTER
EXT_PTR LABEL	DWORD			; POINTER TO EXTENSION
	ORG	400H
DATA_AREA	LABEL	BYTE		; ABSOLUTE LOCATION OF DATA SEGMENT
DATA_WORD	LABEL	WORD
	ORG	0500H
MFG_TEST_RTN	LABEL	FAR
	ORG	7C00H
BOOT_LOCN	LABEL	FAR
ABS0		ENDS

;------------------------------------------------
; STACK -- USED DURING INITIALIZATION ONLY	:
;------------------------------------------------

STACK	SEGMENT AT 30H
	DW	128 DUP(?)
TOS	LABEL	WORD
STACK	ENDS

;----------------------------------------
;	ROM BIOS DATA AREAS		:
;----------------------------------------

DATA	SEGMENT AT 40H
RS232_BASE	DW	4 DUP(?)	; ADDRESSES OF RS232 ADAPTERS
PRINTER_BASE	DW	4 DUP(?)	; ADDRESSES OF PRINTERS
EQUIP_FLAG	DW	?		; INSTALLED HARDWARE
MFG_TST 	DB	?		; INITIALIZATION FLAG
MEMORY_SIZE	DW	?		; MEMORY SIZE IN K BYTES
MFG_ERR_FLAG	DB	?		; SCRATCHPAD FOR MANUFACTURING
		DB	?		; ERROR CODES

;----------------------------------------
;	KEYBOARD DATA AREAS		:
;----------------------------------------

KB_FLAG 	DB	?

;----- SHIFT FLAG EQUATES WITHIN KB_FLAG

INS_STATE	EQU	80H		; INSERT STATE IS ACTIVE
CAPS_STATE	EQU	40H		; CAPS LOCK STATE HAS BEEN TOGGLED
NUM_STATE	EQU	20H		; NUM LOCK STATE HAS BEEN TOGGLED
SCROLL_STATE	EQU	10H		; SCROLL LOCK STATE HAS BEEN TOGGLED
ALT_SHIFT	EQU	08H		; ALTERNATE SHIFT KEY DEPRESSED
CTL_SHIFT	EQU	04H		; CONTROL SHIFT KEY DEPRESSED
LEFT_SHIFT	EQU	02H		; LEFT SHIFT KEY DEPRESSED
RIGHT_SHIFT	EQU	01H		; RIGHT SHIFT KEY DEPRESSED

KB_FLAG_1	DB	?		; SECOND BYTE OF KEYBOARD STATUS

INS_SHIFT	EQU	80H		; INSERT KEY IS DEPRESSED
CAPS_SHIFT	EQU	40H		; CAPS LOCK KEY IS DEPRESSED
NUM_SHIFT	EQU	20H		; NUM LOCK KEY IS DEPRESSED
SCROLL_SHIFT	EQU	10H		; SCROLL LOCK KEY IS DEPRESSED
HOLD_STATE	EQU	08H		; SUSPEND KEY HAS BEEN TOGGLED

ALT_INPUT	DB	?		; STORAGE FOR ALTERNATE KEYPAD ENTRY
BUFFER_HEAD	DW	?		; POINTER TO HEAD OF KEYBOARD BUFFER
BUFFER_TAIL	DW	?		; POINTER TO TAIL OF KEYBOARD BUFFER
KB_BUFFER	DW	16 DUP(?)	; ROOM FOR 15 ENTRIES
KB_BUFFER_END	LABEL	WORD

;------ HEAD = TAIL INDICATES THAT THE BUFFER IS EMPTY

NUM_KEY 	EQU	69		; SCAN CODE FOR NUMBER LOCK
SCROLL_KEY	EQU	70		; SCROLL LOCK KEY
ALT_KEY 	EQU	56		; ALTERNATE SHIFT KEY SCAN CODE
CTL_KEY 	EQU	29		; SCAN CODE FOR CONTROL KEY
CAPS_KEY	EQU	58		; SCAN CODE FOR SHIFT LOCK
LEFT_KEY	EQU	42		; SCAN CODE FOR LEFT SHIFT
RIGHT_KEY	EQU	54		; SCAN CODE FOR RIGHT SHIFT
INS_KEY 	EQU	82		; SCAN CODE FOR INSERT KEY
DEL_KEY 	EQU	83		; SCAN CODE FOR DELETE KEY

;----------------------------------------
;	DISKETTE DATA AREAS		:
;----------------------------------------
SEEK_STATUS	DB	?		; DRIVE RECALIBRATION STATUS
					; BIT 3-0 = DRIVE 3-0 NEEDS RECAL
					; BEFORE NEXT SEEK IF BIT IS = 0

INT_FLAG	EQU	080H		; INTERRUPT OCCURRENCE FLAG
MOTOR_STATUS	DB	?		; MOTOR STATUS
					; BIT 3-0 = DRIVE 3-0 IS CURRENTLY
					;   RUNNING
					; BIT 7 = CURRENT OPERATION IS A WRITE,
					;   REQUIRES DELAY

MOTOR_COUNT	DB	?		; TIME OUT COUNTER FOR DRIVE TURN OFF
MOTOR_WAIT	EQU	37		; 2 SECS OF COUNTS FOR MOTOR TURN OFF

DISKETTE_STATUS DB	?		; RETURN CODE STATUS BYTE
TIME_OUT	EQU	80H		; ATTACHMENT FAILED TO RESPOND
BAD_SEEK	EQU	40H		; SEEK OPERATION FAILED
BAD_NEC 	EQU	20H		; NEC CONTROLLER HAS FAILED
BAD_CRC 	EQU	10H		; BAD CRC ON DISKETTE READ
DMA_BOUNDARY	EQU	09H		; ATTEMPT TO DMA ACROSS 64K BOUNDARY
BAD_DMA 	EQU	08H		; DMA OVERRUN ON OPERATION
RECORD_NOT_FND	EQU	04H		; REQUESTED SECTOR NOT FOUND
WRITE_PROTECT	EQU	03H		; WRITE ATTEMPTED ON WRITE PROT DISK
BAD_ADDR_MARK	EQU	02H		; ADDRESS MARK NOT FOUND
BAD_CMD 	EQU	01H		; BAD COMMAND PASSED TO DISKETTE I/O

NEC_STATUS	DB	7 DUP(?)	; STATUS BYTES FROM NEC

;----------------------------------------
;	VIDEO DISPLAY DATA AREA 	:
;----------------------------------------
CRT_MODE	DB	?		; CURRENT CRT MODE
CRT_COLS	DW	?		; NUMBER OF COLUMNS ON SCREEN
CRT_LEN 	DW	?		; LENGTH OF REGEN IN BYTES
CRT_START	DW	?		; STARTING ADDRESS IN REGEN BUFFER
CURSOR_POSN	DW	8 DUP(?)	; CURSOR FOR EACH OF UP TO 8 PAGES
CURSOR_MODE	DW	?		; CURRENT CURSOR MODE SETTING
ACTIVE_PAGE	DB	?		; CURRENT PAGE BEING DISPLAYED
ADDR_6845	DW	?		; BASE ADDRESS FOR ACTIVE DISPLAY CARD
CRT_MODE_SET	DB	?		; CURRENT SETTING OF THE 3X8 REGISTER
CRT_PALETTE	DB	?		; CURRENT PALETTE SETTING COLOR CARD

;----------------------------------------
;	POST DATA AREA			:
;----------------------------------------
IO_ROM_INIT	DW	?		; PNTR TO OPTIONAL I/O ROM INIT ROUTINE
IO_ROM_SEG	DW	?		; POINTER TO IO ROM SEGMENT
INTR_FLAG	DB	?		; FLAG TO INDICATE AN INTERRUPT HAPPEND

;----------------------------------------
;	TIMER DATA AREA 		:
;----------------------------------------
TIMER_LOW	DW	?		; LOW WORD OF TIMER COUNT
TIMER_HIGH	DW	?		; HIGH WORD OF TIMER COUNT
TIMER_OFL	DB	?		; TIMER HAS ROLLED OVER SINCE LAST READ
;COUNTS_SEC	EQU	18
;COUNTS_MIN	EQU	1092
;COUNTS_HOUR	EQU	65543
;COUNTS_DAY	EQU	1573040 = 1800B0H

;----------------------------------------
;	   SYSTEM DATA AREA		:
;----------------------------------------
BIOS_BREAK	DB	?		; BIT 7 = 1 IF BREAK KEY HAS BEEN HIT
RESET_FLAG	DW	?		; WORD = 1234H IF KEYBOARD RESET UNDERWAY
;----------------------------------------
;	  FIXED DISK DATA AREA		:
;----------------------------------------
		DW	?		;
		DW	?		;
;--------------------------------------------------------
;	PRINTER AND RS232 TIME-OUT VARIABLES		:
;--------------------------------------------------------
PRINT_TIM_OUT	DB	4 DUP(?)
RS232_TIM_OUT	DB	4 DUP(?)
;----------------------------------------
;	ADDITIONAL KEYBOARD DATA AREA	:
;----------------------------------------
BUFFER_START	DW	?
BUFFER_END	DW	?
DATA	ENDS
;----------------------------------------
;	EXTRA DATA AREA 		:
;----------------------------------------
XXDATA	SEGMENT AT 50H
STATUS_BYTE	DB	?
XXDATA		ENDS
;----------------------------------------
;	VIDEO DISPLAY BUFFER		:
;----------------------------------------
VIDEO_RAM	SEGMENT AT 0B800H
REGEN	LABEL	BYTE
REGENW	LABEL	WORD
	DB	16384 DUP(?)
VIDEO_RAM	ENDS
;----------------------------------------
;	  ROM RESIDENT CODE		:
;----------------------------------------
CODE	SEGMENT AT 0F000H
	DB	57344 DUP(?)		; FILL LOWEST 56K

	DB	'1501512 COPR. IBM 1981'        ; COPYRIGHT NOTICE


;------------------------------------------------
;	INITIAL RELIABILITY TESTS -- PHASE 1	:
;------------------------------------------------

	ASSUME	  CS:CODE,SS:CODE,ES:ABS0,DS:DATA

;----------------------------------------
;	DATA DEFINITIONS		:
;----------------------------------------

C1	DW	C11		; RETURN ADDRESS
C2	DW	C24		; RETURN ADDRESS FOR DUMMY STACK

F3B	DB	' KB OK',13     ; KB FOR MEMORY SIZE

;------------------------------------------------------------------------
;	LOAD A BLOCK OF TEST CODE THROUGH THE KEYBOARD PORT		:
;	FOR MANUFACTURING TEST. 					:
;	THIS ROUTINE WILL LOAD A TEST (MAX LENGTH=FAFFH) THROUGH	:
;	THE KEYBOARD PORT. CODE WILL BE LOADED AT LOCATION		:
;	0000:0500. AFTER LOADING, CONTROL WILL BE TRANSFERED		:
;	TO LOCATION 0000:0500. STACK WILL BE LOCATED JUST BELOW 	:
;	THE TEST CODE. THIS ROUTINE ASSUMES THAT THE FIRST 2		:
;	BYTES TRANSFERED CONTAIN THE COUNT OF BYTES TO BE LOADED	:
;	(BYTE 1=COUNT LOW, BYTE 2=COUNT HI.)				:
;------------------------------------------------------------------------

;----- FIRST, GET THE COUNT

MFG_BOOT:
	CALL	SP_TEST 		; GET COUNT LOW
	MOV	BH,BL			; SAVE IT
	CALL	SP_TEST 		; GET COUNT HI
	MOV	CH,BL
	MOV	CL,BH			; CX NOW HAS COUNT
	CLD				; SET DIR. FLAG TO INCREMENT
	CLI
	MOV	DI,0500H		; SET TARGET OFFSET (DS=0000)
	MOV	AL,0FDH 		; UNMASK K/B INTERRUPT
	OUT	INTA01,AL
	MOV	AL,0AH			; SEND READ INT. REQUEST REG. CMD
	OUT	INTA00,AL
	MOV	DX,61H			; SET UP PORT B ADDRESS
	MOV	BX,4CCCH		; CONTROL BITS FOR PORT B
	MOV	AH,02H			; K/B REQUEST PENDING MASK
TST:
	MOV	AL,BL
	OUT	DX,AL			; TOGGLE K/B CLOCK
	MOV	AL,BH
	OUT	DX,AL
	DEC	DX			; POINT DX AT ADDR. 60 (KB DATA)
TST1:
	IN	AL,INTA00		; GET IRR REG
	AND	AL,AH			; KB REQUEST PENDING?
	JZ	TST1			; LOOP TILL DATA PRESENT
	IN	AL,DX			; GET DATA
	STOSB				; STORE IT
	INC	DX			; POINT DX BACK AT PORT B (61)
	LOOP	TST			; LOOP TILL ALL BYTES READ

	JMP	MFG_TEST_RTN		; FAR JUMP TO CODE THAT WAS JUST
					; LOADED

;----------------------------------------
;	8088 PROCESSOR TEST		:
; DESCRIPTION				:
;	VERIFY 8088 FLAGS, REGISTERS	:
;	AND CONDITIONAL JUMPS		:
;----------------------------------------
	ASSUME	CS:CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING
	ORG	0E05BH
RESET	LABEL	FAR
START:	CLI				; DISABLE INTERRUPTS
	MOV	AH,0D5H 		; SET SF, CF, ZF, AND AF FLAGS ON
	SAHF
	JNC	ERR01			; GO TO ERR ROUTINE IF CF NOT SET
	JNZ	ERR01			; GO TO ERR ROUTINE IF ZF NOT SET
	JNP	ERR01			; GO TO ERR ROUTINE IF PF NOT SET
	JNS	ERR01			; GO TO ERR ROUTINE IF SF NOT SET
	LAHF				; LOAD FLAG IMAGE TO AH
	MOV	CL,5			; LOAD CNT REG WITH SHIFT CNT
	SHR	AH,CL			; SHIFT AF INTO CARRY BIT POS
	JNC	ERR01			; SO TO ERR ROUTINE IF AF NOT SET
	MOV	AL,40H			; SET THE OF FLAG ON
	SHL	AL,1			; SETUP FOR TESTING
	JNO	ERR01			; GO TO ERR ROUTINE IF OF NOT SET
	XOR	AH,AH			; SET AH = 0
	SAHF				; CLEAR SF, CF, ZF, AND PF
	JBE	ERR01			; GO TO ERR ROUTINE IF CF ON
					; OR TO TO ERR ROUTINE IF ZF ON
	JS	ERR01			; GO TO ERR ROUTINE IF SF ON
	JP	ERR01			; GO TO ERR ROUTINE IF PF ON
	LAHF				; LOAD FLAG IMAGE TO AH
	MOV	CL,5			; LOAD CNT REG WITH SHIFT CNT
	SHR	AH,CL			; SHIFT AH INTO CARRY BIT POS
	JC	ERR01			; GO TO ERR ROUTINE IF ON
	SHL	AH,1			; CHECK THAT OF IS CLEAR
	JO	ERR01			; GO TO ERR ROUTINE IF ON

;----- READ/WRITE THE 8088 GENERAL AND SEGMENTATION REGISTERS
;      WITH ALL ONE'S AND ZEROES'S.

	MOV	AX,0FFFFH		; SETUP ONE'S PATTERN IN AX
	STC
C8:	MOV	DS,AX			; WRITE PATTERN TO ALL REGS
	MOV	BX,DS
	MOV	ES,BX
	MOV	CX,ES
	MOV	SS,CX
	MOV	DX,SS
	MOV	SP,DX
	MOV	BP,SP
	MOV	SI,BP
	MOV	DI,SI
	JNC	C9			; TST1A
	XOR	AX,DI			; PATTERN MAKE IT THRU ALL REGS
	JNZ	ERR01			; NO - GO TO ERR ROUTINE
	CLC
	JMP	C8
C9:					; TST1A
	OR	AX,DI			; ZERO PATTERN MAKE IT THRU?
	JZ	C10			; YES - GO TO NEXT TEST
ERR01:	HLT				; HALT SYSTEM
;----------------------------------------
;	ROS CHECKSUM TEST I		:
;DESCRIPTION				:
;	A CHECKSUM IS DONE FOR THE 8K	:
;	ROS MODULE CONTAINING POD AND	:
;	BIOS.				:
;----------------------------------------
C10:
					; ZERO IN AL ALREADY
	OUT	0A0H,AL 		; DISABLE NMI INTERRUPTS
	OUT	83H,AL			; INITIALIZE DMA PAGE REG
	MOV	DX,3D8H
	OUT	DX,AL			; DISABLE COLOR VIDEO
	INC	AL
	MOV	DL,0B8H
	OUT	DX,AL			; DISABLE B/W VIDEO,EN HIGH RES
	MOV	AL,89H			; SET 8255 FOR B,A=OUT, C=IN
	OUT	CMD_PORT,AL
	MOV	AL,10100101B
					; ENABLE PARITY CHECKERS AND
	OUT	PORT_B,AL		; PULL KB CLOCK HI, TRI-STATE
					; KEYBOARD INPUTS,ENABLE HIGH
					; BANK OF SWITCHES->PORT C(0-3)
	MOV	AL,01H			; <><><><><><><><><><><><>
	OUT	PORT_A,AL		; <><><>CHECKPOINT 1<><><>
	MOV	AX,CS			; SETUP SS SEG REG
	MOV	SS,AX
	MOV	DS,AX			; SET UP DATA SEG TO POINT TO
					; ROM ADDRESS
	CLD				; SET DIRECTION FLAG TO INC.
	ASSUME	SS:CODE
	MOV	BX,0E000H		; SETUP STARTING ROS ADDR
	MOV	SP,OFFSET C1		; SETUP RETURN ADDRESS
	JMP	ROS_CHECKSUM
C11:	JNE	ERR01			; HALT SYSTEM IF ERROR
;--------------------------------------------------------
;	8237 DMA INITIALIZATION CHANNEL REGISTER TEST	:
;DESCRIPTION						:
;	DISABLE THE 8237 DMA CONTROLLER.  VERIFY THAT	:
;	TIMER 1 FUNCTIONS OK. WRITE/READ THE CURRENT	:
;	ADDRESS AND WORD COUNT REGISTERS FOR ALL	:
;	CHANNELS.  INITIALIZE AND START DMA FOR MEMORY	:
;	REFRESH.					:
;--------------------------------------------------------

;----- DISABLE DMA CONTROLLER

	MOV	AL,02H			; <><><><><><><><><><><><>
	OUT	PORT_A,AL		; <><><>CHECKPOINT 2<><><>
	MOV	AL,04			; DISABLE DMA CONTROLLER
	OUT	DMA08,AL

;----- VERIFY THAT TIMER 1 FUNCTIONS OK

	MOV	AL,54H			; SEL TIMER 1,LSB,MODE 2
	OUT	TIMER+3,AL
	MOV	AL,CL			; SET INITIAL TIMER CNT TO 0
	OUT	TIMER+1,AL
C12:					; TIMER1_BITS_ON
	MOV	AL,40H			; LATCH TIMER 1 COUNT
	OUT	TIMER+3,AL
	CMP	BL,0FFH 		; YES - SEE IF ALL BITS GO OFF
	JE	C13			; TIMER1_BITS_OFF
	IN	AL,TIMER+1		; READ TIMER 1 COUNT
	OR	BL,AL			; ALL BITS ON IN TIMER
	LOOP	C12			; TIMER1_BITS_ON
	HLT				; TIMER 1 FAILURE, HALT SYS
C13:					; TIMER1_BITS_OFF
	MOV	AL,BL			; SET TIMER 1 CNT
	SUB	CX,CX
	OUT	TIMER+1,AL
C14:					; TIMER_LOOP
	MOV	AL,40H			; LATCH TIMER 1 COUNT
	OUT	TIMER+3,AL
	NOP				; DELAY FOR TIMER
	NOP
	IN	AL,TIMER+1		; READ TIMER 1 COUNT
	AND	BL,AL
	JZ	C15			; GO TO WRAP_DMA_REG
	LOOP	C14			; TIMER_LOOP
	HLT				; TIMER ERROR - HALT SYSTEM

;----- INITIALIZE TIMER 1 TO REFRESH MEMORY

C15:	MOV	AL,03H			; <><><><><><><><><><><><>
	OUT	PORT_A,AL		; <><><>CHECKPOINT 3<><><>
					; WRAP_DMA_REG
	OUT	DMA+0DH,AL		; SEND MASTER CLEAR TO DMA

;----- WRAP DMA CHANNELS ADDRESS AND COUNT REGISTERS

	MOV	AL,0FFH 		; WRITE PATTERN FF TO ALL REGS
C16:	MOV	BL,AL			; SAVE PATTERN FOR COMPARE
	MOV	BH,AL
	MOV	CX,8			; SETUP LOOP CNT
	MOV	DX,DMA			; SETUP I/O PORT ADDR OF REG
C17:	OUT	DX,AL			; WRITE PATTERN TO REG, LSB
	PUSH	AX			; SATISIFY 8237 I/O TIMINGS
	OUT	DX,AL			; MSB OF 16 BIT REG
	MOV	AL,01H			; AL TO ANOTHER PAT BEFORE RD
	IN	AL,DX			; READ 16-BIT DMA CH REG, LSB
	MOV	AH,AL			; SAVE LSB OF 16-BIT REG
	IN	AL,DX			; READ MSB OF DMA CH REG
	CMP	BX,AX			; PATTERN READ AS WRITTEN?
	JE	C18			; YES - CHECK NEXT REG
	HLT				; NO - HALT THE SYSTEM
C18:					; NXT_DMA_CH
	INC	DX			; SET I/O PORT TO NEXT CH REG
	LOOP	C17			; WRITE PATTERN TO NEXT REG
	INC	AL			; SET PATTERN TO 0
	JZ	C16			; WRITE TO CHANNEL REGS

;----- INITIALIZE AND START DMA FOR MEMORY REFRESH.

	MOV	DS,BX			; SET UP ABS0 INTO DS AND ES
	MOV	ES,BX
	ASSUME	DS:ABS0,ES:ABS0
	MOV	AL,0FFH 		; SET CNT OF 64K FOR RAM REFRESH
	OUT	DMA+1,AL
	PUSH	AX
	OUT	DMA+1,AL
	MOV	AL,058H 		; SET DMA MODE,CH 0,RD.,AUTOINT
	OUT	DMA+0BH,AL		; WRITE DMA MODE REG
	MOV	AL,0			; ENABLE DMA CONTROLLER
	MOV	CH,AL			; SET COUNT HIGH=00
	OUT	DMA+8,AL		; SETUP DMA COMMAND REG
	PUSH	AX
	OUT	DMA+10,AL		; ENABLE DMA CH 0
	MOV	AL,18			; START TIMER 1
	OUT	TIMER+1,AL
	MOV	AL,41H			; SET MODE FOR CHANNEL 1
	OUT	DMA+0BH,AL
	PUSH	AX
	IN	AL,DMA+08		; GET DMA STATUS
	AND	AL,00010000B		; IS TIMER REQUEST THERE?
	JZ	C18C			; (IT SHOULD'T BE)
	HLT				; HALT SYS.(HOT TIMER 1 OUTPUT)
C18C:	MOV	AL,42H			; SET MODE FOR CHANNEL 2
	OUT	DMA+0BH,AL
	MOV	AL,43H			; SET MODE FOR CHANNEL 3
	OUT	DMA+0BH,AL
;------------------------------------------------
;	BASE 16K READ/WRITE STORAGE TEST	:
;DESCRIPTION					:
;	WRITE/READ/VERIFY DATA PATTERNS 	:
;	FF,55,AA,01, AND 00 TO 1ST 32K OF	:
;	STORAGE. VERIFY STORAGE ADDRESSABILITY. :
;------------------------------------------------

;----- DETERMINE MEMORY SIZE AND FILL MEMORY WITH DATA

	MOV	DX,0213H		; ENABLE EXPANSION BOX
	MOV	AL,01H
	OUT	DX,AL

	MOV	BX,DATA_WORD[OFFSET RESET_FLAG] ; SAVE 'RESET_FLAG' IN BX
	MOV	CX,2000H		; SET FOR 16K WORDS
	CMP	BX,1234H		; WARM START?
	JE	CLR_STG
	MOV	SP,OFFSET C2
	JMP	STGTST_CNT
C24:	JE	HOW_BIG 		; STORAGE OK, DETERMINE SIZE
	MOV	BL,AL			; SAVE FAILING BIT PATTERN
	MOV	AL,04H			; <><><><><><><><><><><><>
C24A:	OUT	PORT_A,AL		; <><><>CHECKPOINT 4<><><>
	SUB	CX,CX			; BASE RAM FAILURE - HANG
C24B:	LOOP	C24B			; FLIPPING BETWEEN 04 AND
	XCHG	BL,AL			; FAILING BIT PATTERN
	JMP	C24A
CLR_STG:
	SUB	AX,AX			; MAKE AX=0000
	REP	STOSW			; STORE 8K WORDS OF 0000
HOW_BIG:
	MOV	DATA_WORD[OFFSET RESET_FLAG],BX ; RESTORE RESET FLAG
	MOV	DX,0400H		; SET POINTER TO JUST>16KB
	MOV	BX,16			; BASIC COUNT OF 16K
FILL_LOOP:
	MOV	ES,DX			; SET SEG. REG.
	SUB	DI,DI
	MOV	AX,0AA55H		; TEST PATTERN
	MOV	CX,AX			; SAVE PATTERN
	MOV	ES:[DI],AX		; SEND PATTERN TO MEM.
	MOV	AL,0FH			; PUT SOMETHING IN AL
	MOV	AX,ES:[DI]		; GET PATTERN
	XOR	AX,CX			; COMPARE PATTERNS
	JNZ	HOW_BIG_END		; GO END IF NO COMPARE
	MOV	CX,2000H		; SET COUNT FOR 8K WORDS
	REP	STOSW			; FILL 8K WORDS
	ADD	DX,400H 		; POINT TO NEXT 16KB BLOCK
	ADD	BX,16			; BUMP COUNT BY 16KB
	CMP	DH,0A0H 		; TOP OF RAM AREA YET? (A0000)
	JNZ	FILL_LOOP
HOW_BIG_END:
	MOV	DATA_WORD[OFFSET MEMORY_SIZE],BX	; SAVE MEMORY SIZE

;----- SETUP STACK SEG AND SP

	MOV	AX,STACK		; GET STACK VALUE
	MOV	SS,AX			; SET THE STACK UP
	MOV	SP,OFFSET TOS		; STACK IS READY TO GO
;--------------------------------------------------------
;	INITIALIZE THE 8259 INTERRUPT CONTROLLER CHIP	:
;--------------------------------------------------------
C25:	MOV	AL,13H			; ICW1 - EDGE, SNGL, ICW4
	OUT	INTA00,AL
	MOV	AL,8			; SETUP ICW2 - INT TYPE 8 (8-F)
	OUT	INTA01,AL
	MOV	AL,9			; SETUP ICW4 - BUFFRD,8086 MODE
	OUT	INTA01,AL
	MOV	AL,0FFH 		; MASK ALL INTS. OFF
	OUT	INTA01,AL		; (VIDEO ROUTINE ENABLES INTS.)

;----- SET UP THE INTERRUPT VECTORS TO TEMP INTERRUPT

	PUSH	DS
	MOV	CX,32			; FILL ALL 32 INTERRUPTS
	SUB	DI,DI			; FIRST INTERRUPT LOCATION
	MOV	ES,DI			; SET ES=0000 ALSO
D3:	MOV	AX,OFFSET D11		; MOVE ADDR OF INTR PROC TO TBL
	STOSW
	MOV	AX,CS			; GET ADDR OF INTR PROC SEG
	STOSW
	LOOP	D3			; VECTBL0

;----- ESTABLISH BIOS SUBROUTINE CALL INTERRUPT VECTORS

	MOV	DI,OFFSET VIDEO_INT	; SETUP ADDR TO INTR AREA
	PUSH	CS
	POP	DS			; SETUP ADDR OF VECTOR TABLE
	MOV	AX,DS			; SET AX=SEGMENT
	MOV	SI,OFFSET VECTOR_TABLE+16	; START WITH VIDEO ENTRY
	MOV	CX,16
D3A:	MOVSW				; MOVE VECTOR TABLE TO RAM
	INC	DI			; SKIP SEGMENT POINTER
	INC	DI
	LOOP	D3A
;------------------------------------------------
;	DETERMINE CONFIGURATION AND MFG. MODE	:
;------------------------------------------------

	POP	DS
	PUSH	DS			; RECOVER DATA SEG POINTER
	IN	AL,PORT_C		; GET SWITCH INFO
	AND	AL,00001111B		; ISOLATE SWITCHES
	MOV	AH,AL			; SAVE
	MOV	AL,10101101B		; ENABLE OTHER BANK OF SWS.
	OUT	PORT_B,AL
	NOP
	IN	AL,PORT_C
	MOV	CL,4
	ROL	AL,CL			; ROTATE TO HIGH NIBBLE
	AND	AL,11110000B		; ISOLATE
	OR	AL,AH			; COMBINE WITH OTHER BANK
	SUB	AH,AH
	MOV	DATA_WORD[OFFSET EQUIP_FLAG],AX ; SAVE SWITCH INFO
	MOV	AL,99H
	OUT	CMD_PORT,AL
	CALL	KBD_RESET		; SEE IF MFG. JUMPER IN
	CMP	BL,0AAH 		; KEYBOARD PRESENT?
	JE	E6
	CMP	BL,065H 		; LOAD MFG. TEST REQUEST?
	JNE	D3B
	JMP	MFG_BOOT		; GO TO BOOTSTRAP IF SO
D3B:	MOV	AL,38H
	OUT	PORT_B,AL
	NOP
	NOP
	IN	AL,PORT_A
	AND	AL,0FFH 		; WAS DATA LINE GROUNDED
	JNZ	E6
	INC	DATA_AREA[OFFSET MFG_TST]	; SET MANUFACTURING TEST FLAG

;--------------------------------------------------------
;	INITIALIZE AND START CRT CONTROLLER (6845)	:
;	TEST VIDEO READ/WRITE STORAGE.			:
;DESCRIPTION						:
;	RESET THE VIDEO ENABLE SIGNAL.			:
;	SELECT ALPHANUMERIC MODE, 40 * 25, B & W.	:
;	READ/WRITE DATA PATTERNS TO STG. CHECK STG	:
;	ADDRESSABILITY. 				:
; ERROR = 1 LONG AND 2 SHORT BEEPS			:
;--------------------------------------------------------
E6:
	MOV	AX,DATA_WORD[OFFSET EQUIP_FLAG] ; GET SENSE SWITCH INFO
	PUSH	AX			; SAVE IT
	MOV	AL,30H
	MOV	DATA_WORD[OFFSET EQUIP_FLAG],AX
	SUB	AH,AH
	INT	10H			; SEND INIT TO B/W CARD
	MOV	AL,20H
	MOV	DATA_WORD[OFFSET EQUIP_FLAG],AX
	SUB	AH,AH
	INT	10H			; AND INIT COLOR CARD
	POP	AX			; RECOVER REAL SWITCH INFO
	MOV	DATA_WORD[OFFSET EQUIP_FLAG],AX ; RESTORE IT
					; AND CONTINUE
	AND	AL,30H			; ISOLATE VIDEO SWS
	JNZ	E7			; VIDEO SWS SET TO 0?
	MOV	DI,OFFSET VIDEO_INT	; SET INT 10H TO DUMMY
	MOV	[DI],OFFSET DUMMY_RETURN	; RETURN IF NO VIDEO CARD
	JMP	E18_1			; BYPASS VIDEO TEST
E7:					; TEST_VIDEO:
	CMP	AL,30H			; B/W CARD ATTACHED?
	JE	E8			; YES - SET MODE FOR B/W CARD
	INC	AH			; SET COLOR MODE FOR COLOR CD
	CMP	AL,20H			; 80X25 MODE SELECTED?
	JNE	E8			; NO - SET MODE FOR 40X25
	MOV	AH,3			; SET MODE FOR 80X25
E8:	XCHG	AH,AL			; SET_MODE:
	PUSH	AX			; SAVE VIDEO MODE ON STACK
	SUB	AH,AH			; INITIALIZE TO ALPHANUMERIC MD
	INT	10H			; CALL VIDEO_IO
	POP	AX			; RESTORE VIDEO SENSE SWS IN AH
	PUSH	AX			; RESAVE VALUE
	MOV	BX,0B000H		; BEG VIDEO RAM ADDR B/W CD
	MOV	DX,3B8H 		; MODE REG FOR B/W
	MOV	CX,2048 		; RAM WORD CNT FOR B/W CD
	MOV	AL,1			; SET MODE FOR B/W CARD
	CMP	AH,30H			; B/W VIDEO CARD ATTACHED?
	JE	E9			; YES - GO TEST VIDEO STG
	MOV	BH,0B8H 		; BEG VIDEO RAM ADDR COLOR CD
	MOV	DX,3D8H 		; MODE REG FOR COLOR CD
	MOV	CH,20H			; RAM WORD CNT FOR COLOR CD
	DEC	AL			; SET MODE TO 0 FOR COLOR CD
E9:					; TEST_VIDEO_STG:
	OUT	DX,AL			; DISABLE VIDEO FOR COLOR CD
	CMP	DATA_WORD[OFFSET RESET_FLAG],1234H ; POD INIT BY KBD RESET?
	MOV	ES,BX			; POINT ES TO VIDEO RAM STG
	JE	E10			; YES - SKIP VIDEO RAM TEST
	MOV	DS,BX			; POINT DS TO VIDEO RAM STG
	ASSUME	DS:NOTHING,ES:NOTHING
	CALL	STGTST_CNT		; GO TEST VIDEO R/W STG
	JNE	E17			; R/W STG FAILURE - BEEP SPK
;------------------------------------------------
;	SETUP VIDEO DATA ON SCREEN FOR VIDEO	:
;	LINE TEST.				:
;DESCRIPTION					:
;	ENABLE VIDEO SIGNAL AND SET MODE.	:
;	DISPLAY A HORIZONTAL BAR ON SCREEN.	:
;------------------------------------------------
E10:
	POP	AX			; GET VIDEO SENSE SWS (AH)
	PUSH	AX			; SAVE IT
	MOV	AH,0			; ENABLE VIDEO AND SET MODE
	INT	10H			; VIDEO
	MOV	AX,7020H		; WRT BLANKS IN REVERSE VIDEO

;----- UNNATURAL ACT FOR ADDRESS COMPATIBILITY

	JMP	SHORT E10A
	ORG	0E2C3H
	JMP	NMI_INT

E10A:
	SUB	DI,DI			; SETUP STARTING LOC
	MOV	CX,40			; NO. OF BLANKS TO DISPLAY
	REP	STOSW			; WRITE VIDEO STORAGE
;----------------------------------------
;	CRT INTERFACE LINES TEST	:
;DESCRIPTION				:
;	SENSE ON/OFF TRANSITION OF THE	:
;	VIDEO ENABLE AND HORIZONTAL	:
;	SYNC LINES.			:
;----------------------------------------
	POP	AX			; GET VIDEO SENSE SW INFO
	PUSH	AX			; SAVE IT
	CMP	AH,30H			; B/W CARD ATTACHED?
	MOV	DX,03BAH		; SETUP ADDR OF BW STATUS PORT
	JE	E11			; YES - GO TEST LINES
	MOV	DX,03DAH		; COLOR CARD IS ATTACHED
E11:					; LINE_TST:
	MOV	AH,8
E12:					; OFLOOP_CNT:
	SUB	CX,CX
E13:
	IN	AL,DX			; READ CRT STATUS PORT
	AND	AL,AH			; CHECK VIDEO/HORZ LINE
	JNZ	E14			; ITS ON - CHECK IF IT GOES OFF
	LOOP	E13			; LOOP TILL ON OR TIMEOUT
	JMP	SHORT E17		; GO PRINT ERROR MSG
E14:
	SUB	CX,CX
E15:
	IN	AL,DX			; READ CRT STATUS PORT
	AND	AL,AH			; CHECK VIDEO/HORZ LINE
	JZ	E16			; ITS ON - CHECK NEXT LINE
	LOOP	E15			; LOOP IF OFF TILL IT GOES ON
E17:					; CRT_ERR:
	POP	DS
	PUSH	DS
	MOV	DS:MFG_ERR_FLAG,06H	; <><><>CRT ERR CHKPT. 06<><><>
	MOV	DX,102H
	CALL	ERR_BEEP		; GO BEEP SPEAKER
	JMP	SHORT E18
E16:					; NXT_LINE:
	MOV	CL,3			; GET NEXT BIT TO CHECK
	SHR	AH,CL			;
	JNZ	E12			; GO CHECK HORIZONTAL LINE
E18:					;  DISPLAY_CURSOR:
	POP	AX			; GET VIDEO SENSE SWS (AH)
	MOV	AH,0			; SET MODE AND DISPLAY CURSOR
	INT	10H			; CALL VIDEO I/O PROCEDURE
E18_1:
	MOV	DX,0C000H		; SEE IF ADVANCED VIDEO CARD
E18A:
	MOV	DS,DX			; IS PRESENT
	SUB	BX,BX
	MOV	AX,[BX] 		; GET FIRST 2 LOCATIONS
	PUSH	BX
	POP	BX			; LET BUS SETTLE
	CMP	AX,0AA55H		; PRESENT?
	JNZ	E18B			; NO? GO LOOK FOR OTHER MODULES
	CALL	ROM_CHECK		; GO SCAN MODULE
	JMP	SHORT E18C
E18B:
	ADD	DX,0080H		; POINT TO NEXT 2K BLOCK
E18C:
	CMP	DX,0C800H		; TOP OF VIDEO ROM AREA YET?
	JL	E18A			; GO SCAN FOR ANOTHER MODULE
;--------------------------------------------------------
;	8259 INTERRUPT CONTROLLER TEST			:
;DESCRIPTION						:
;	READ/WRITE THE INTERRUPT MASK REGISTER (IMR)	:
;	WITH ALL ONES AND ZEROES. ENABLE SYSTEM 	:
;	INTERRUPTS.  MASK DEVICE INTERRUPTS OFF. CHECK	:
;	FOR HOT INTERRUPTS (UNEXPECTED).		:
;--------------------------------------------------------
	ASSUME	DS:ABS0
C21:	POP	DS

;----- TEST THE IMR REGISTER

C21A:	MOV	DATA_AREA[OFFSET MFG_ERR_FLAG],05H
					; <><><><><><><><><><><><>
					; <><><>CHECKPOINT 5<><><>
	MOV	AL,0			; SET IMR TO ZERO
	OUT	INTA01,AL
	IN	AL,INTA01		; READ IMR
	OR	AL,AL			; IMR = 0?
	JNZ	D6			; GO TO ERR ROUTINE IF NOT 0
	MOV	AL,0FFH 		; DISABLE DEVICE INTERRUPTS
	OUT	INTA01,AL		; WRITE TO IMR
	IN	AL,INTA01		; READ IMR
	ADD	AL,1			; ALL IMR BITS ON?
	JNZ	D6			; NO - GO TO ERR ROUTINE

;----- CHECK FOR HOT INTERRUPTS

;----- INTERRUPTS ARE MASKED OFF.  CHECK THAT NO INTERRUPTS OCCUR

	MOV	DATA_AREA[OFFSET INTR_FLAG],AL	; CLEAR INTERRUPT FLAG
	STI				; ENABLE EXTERNAL INTERRUPTS
	SUB	CX,CX			; WAIT 1 SEC FOR ANY INTRS THAT
D4:
	LOOP	D4			; MIGHT OCCUR
D5:
	LOOP	D5
	CMP	DATA_AREA[OFFSET INTR_FLAG],00H ; DID ANY INTERRUPTS OCCUR?
	JZ	D7			; NO - GO TO NEXT TEST
D6:
	MOV	SI,OFFSET E0		; DISPLAY 101 ERROR
	CALL	E_MSG
	CLI
	HLT				; HALT THE SYSTEM
;--------------------------------------------------------
;	8253 TIMER CHECKOUT				:
;DESCRIPTION						:
;	VERIFY THAT THE SYSTEM TIMER (0) DOESN'T COUNT  :
;	TOO FAST OR TOO SLOW.				:
;--------------------------------------------------------
D7:
	MOV	DATA_AREA[OFFSET MFG_ERR_FLAG],02H
					; <><><><><><><><><><><><><><><><>
					; <><><>TIMER CHECKPOINT (2)<><><>
	MOV	AL,0FEH 		; MASK ALL INTRS EXCEPT LVL 0
	OUT	INTA01,AL		; WRITE THE 8259 IMR
	MOV	AL,00010000B		; SEL TIM 0, LSB, MODE 0, BINARY
	OUT	TIM_CTL,AL		; WRITE TIMER CONTROL MODE REG
	MOV	CX,16H			; SET PGM LOOP CNT
	MOV	AL,CL			; SET TIMER 0 CNT REG
	OUT	TIMER0,AL		; WRITE TIMER 0 CNT REG
D8:
	TEST	DATA_AREA[OFFSET INTR_FLAG],01H
					; DID TIMER 0 INTERRUPT OCCUR?
	JNZ	D9			; YES - CHECK TIMER OP FOR SLOW TIME
	LOOP	D8			; WAIT FOR INTR FOR SPECIFIED TIME
	JMP	D6			; TIMER 0 INTR DIDN'T OCCUR - ERR
D9:
	MOV	CL,12			; SET PGM LOOP CNT
	MOV	AL,0FFH 		; WRITE TIMER 0 CNT REG
	OUT	TIMER0,AL
	MOV	DATA_AREA[OFFSET INTR_FLAG],0	; RESET INTR RECEIVED FLAG
	MOV	AL,0FEH 		; REENABLE TIMER 0 INTERRUPTS
	OUT	INTA01,AL
D10:
	TEST	DATA_AREA[OFFSET INTR_FLAG],01H ; DID TIMER 0 INTERRUPT OCCUR?
	JNZ	D6			; YES - TIMER CNTING TOO FAST, ERR
	LOOP	D10			; WAIT FOR INTR FOR SPECIFIED TIME

;----- SETUP TIMER 0 TO MODE 3

	MOV	AL,0FFH 		; DISABLE ALL DEVICE INTERRUPTS
	OUT	INTA01,AL
	MOV	AL,36H			; SEL TIM 0,LSB,MSB,MODE 3
	OUT	TIMER+3,AL		; WRITE TIMER MODE PEG
	MOV	AL,0
	OUT	TIMER,AL		; WRITE LSB TO TIMER 0 REG
	OUT	TIMER,AL		; WRITE MSB TO TIMER 0 REG
;------------------------------------------------
;	KEYBOARD TEST				:
;DESCRIPTION					:
;	RESET THE KEYBOARD AND CHECK THAT SCAN	:
;	CODE 'AA' IS RETURNED TO THE CPU.       :
;	CHECK FOR STUCK KEYS			:
;------------------------------------------------
TST12:
	MOV	AL,99H			; SET 8255 MODE A,C=IN B=OUT
	OUT	CMD_PORT,AL
	MOV	AL,DATA_AREA[OFFSET EQUIP_FLAG]
	AND	AL,01			; TEST CHAMBER?
	JZ	F7			; BYPASS IF SO
	CMP	DATA_AREA[OFFSET MFG_TST],1	; MANUFACTURING TEST MODE?
	JE	F7			; YES - SKIP KEYBOARD TEST
	CALL	KBD_RESET		; ISSUE RESET TO KEYBRD
	JCXZ	F6			; PRINT ERR MSG IF NO INTERRUPT
	MOV	AL,49H			; ENABLE KEYBOARD
	OUT	PORT_B,AL
	CMP	BL,0AAH 		; SCAN CODE AS EXPECTED?
	JNE	F6			; NO - DISPLAY ERROR MSG

;----- CHECK FOR STUCK KEYS

	MOV	AL,0C8H 		; CLR KBD, SET CLK LINE HIGH
	OUT	PORT_B,AL
	MOV	AL,48H			; ENABLE KBD,CLK IN NEXT BYTE
	OUT	PORT_B,AL
	SUB	CX,CX
F5:					; KBD_WAIT:
	LOOP	F5			; DELAY FOR A WHILE
	IN	AL,KBD_IN		; CHECK FOR STUCK KEYS
	CMP	AL,0			; SCAN CODE = 0?
	JE	F7			; YES - CONTINUE TESTING
	CALL	XPC_BYTE		; CONVERT AND PRINT
F6:
	MOV	SI,OFFSET F1		; GET MSG ADDR
	CALL	E_MSG			; PRINT MSG ON SCREEN
;------------------------------------------------
;	SETUP HARDWARE INT. VECTOR TABLE	:
;------------------------------------------------
F7:
	PUSH	DS			; SETUP_INT_TABLE:
	SUB	AX,AX
	MOV	ES,AX
	MOV	CX,08			; GET VECTOR CNT
	PUSH	CS			; SETUP DS SEG REG
	POP	DS
	MOV	SI,OFFSET VECTOR_TABLE
	MOV	DI,OFFSET INT_PTR
F7A:
	MOVSW
	INC	DI			; SKIP OVER SEGMENT
	INC	DI
	LOOP	F7A
	POP	DS

;----- SET UP OTHER INTERRUPTS AS NECESSARY

	MOV	NMI_PTR,OFFSET NMI_INT	; NMI INTERRUPT
	MOV	INT5_PTR,OFFSET PRINT_SCREEN	; PRINT SCREEN
	MOV	BASIC_PTR+2,0F600H	; SEGMENT FOR CASSETTE BASIC

;----- SETUP TIMER 0 TO BLINK LED IF MANUFACTURING TEST MODE

	CMP	DATA_AREA[OFFSET MFG_TST],01H	; MFG. TEST MODE?
	JNZ	EXP_IO
	MOV	DS: WORD PTR(1CH*4),OFFSET BLINK_INT; SETUP TIMER INTR TO BLINK LED
	MOV	AL,0FEH 		; ENABLE TIMER INTERRUPT
	OUT	INTA01,AL
;----------------------------------------------------------------
; EXPANSION I/O BOX TEST					:
;	CHECK TO SEE IF EXPANSION BOX PRESENT - IF INSTALLED,	:
;	TEST DATA AND ADDRESS BUSES TO I/O BOX			:
;  ERROR='1801'                                                 :
;----------------------------------------------------------------

;----- DETERMINE IF BOX IS PRESENT

EXP_IO: 				; (CARD WAS ENABLED EARLIER)
	MOV	DX,0210H		; CONTROL PORT ADDRESS
	MOV	AX,5555H		; SET DATA PATTERN
	OUT	DX,AL
	MOV	AL,01H
	IN	AL,DX			; RECOVER DATA
	CMP	AL,AH			; REPLY?
	JNE	E19			; NO RESPONSE. GO TO NEXT TEST
	NOT	AX			; MAKE DATA=AAAA
	OUT	DX,AL
	MOV	AL,01H
	IN	AL,DX			; RECOVER DATA
	CMP	AL,AH
	JNE	E19			; NO ANSWER=NEXT TEST

;----- CHECK ADDRESS BUS

EXP2:
	MOV	BX,0001H
	MOV	DX,0215H		; LOAD HI. ADDR REG ADDRESS
	MOV	CX,0016 		; GO ACROSS 16 BITS
EXP3:
	MOV	CS:[BX],AL		; WRITE ADDRESS F0000+BX
	NOP
	IN	AL,DX			; READ ADDR. HIGH
	CMP	AL,BH
	JNE	EXP_ERR 		; GO ERROR IF MISCOMPARE
	INC	DX			; DX-216H (ADDR. LOW REG)
	IN	AL,DX
	CMP	AL,BL			; COMPARE TO LOW ADDRESS
	JNE	EXP_ERR
	DEC	DX			; DX BACK TO 215H
	SHL	BX,1
	LOOP	EXP3			; LOOP TILL '1' WALKS ACROSS BX

;----- CHECK DATA BUS

	MOV	CX,0008 		; DO 8 TIMES
	MOV	AL,01
	DEC	DX			; MAKE DX=214H (DATA BUS REG)
EXP4:
	MOV	AH,AL			; SAVE DATA BUS VALUE
	OUT	DX,AL			; SEND VALUE TO REG
	MOV	AL,01H
	IN	AL,DX			; RETRIVE VALUE FROM REG
	CMP	AL,AH			; = TO SAVED VALUE
	JNE	SHORT EXP_ERR
	SHL	AL,1			; FORM NEW DATA PATTERN
	LOOP	EXP4			; LOOP TILL BIT WALKS ACROSS AL
	JMP	SHORT E19		; GO ON TO NEXT TEST
EXP_ERR:
	MOV	SI,OFFSET F3C
	CALL	E_MSG
;--------------------------------------------------------
;	ADDITIONAL READ/WRITE STORAGE TEST		:
;DESCRIPTION						:
;	WRITE/READ DATA PATTERNS TO ANY READ/WRITE	:
;	STORAGE AFTER THE FIRST 32K.  STORAGE		:
;	ADDRESSABILITY IS CHECKED.			:
;--------------------------------------------------------
	ASSUME	DS:DATA
E19:
	CALL	DDS
	PUSH	DS
E20:
	CMP	RESET_FLAG,1234H	; WARM START?
	JNE	E20A			; CONTINUE TEST IF NOT
	JMP	ROM_SCAN		; GO TO NEXT ROUTINE IF SO
E20A:
	MOV	AX,16			; STARTING AMT. OF MEMORY OK
	JMP	SHORT PRT_SIZ		; POST MESSAGE
E20B:
	MOV	BX,MEMORY_SIZE		; GET MEM. SIZE WORD
	SUB	BX,16			; 1ST 16K ALREADY DONE
	MOV	CL,04H
	SHR	BX,CL			; DIVIDE BY 16
	MOV	CX,BX			; SAVE COUNT OF 16K BLOCKS
	MOV	BX,0400H		; SET PTR. TO RAM SEGMENT>16K
E21:
	MOV	DS,BX			; SET SEG. REG
	MOV	ES,BX
	ADD	BX,0400H		; POINT TO NEXT 16K
	PUSH	DX
	PUSH	CX			; SAVE WORK REGS
	PUSH	BX
	PUSH	AX
	MOV	CX,2000H		; SET COUNT FOR 8K WORDS
	CALL	STGTST_CNT
	JNZ	E21A			; GO PRINT ERROR
	POP	AX			; RECOVER TESTED MEM NUMBER
	ADD	AX,16
PRT_SIZ:
	PUSH	AX
	MOV	BX,10			; SET UP FOR DECIMAL CONVERT
	MOV	CX,3			; OF 3 NIBBLES
DECIMAL_LOOP:
	XOR	DX,DX
	DIV	BX			; DIVIDE BY 10
	OR	DL,30H			; MAKE INTO ASCII
	PUSH	DX			; SAVE
	LOOP	DECIMAL_LOOP
	MOV	CX,3
PRT_DEC_LOOP:
	POP	AX			; RECOVER A NUMBER
	CALL	PRT_HEX
	LOOP	PRT_DEC_LOOP
	MOV	CX,7
	MOV	SI,OFFSET F3B		; PRINT ' KB OK'
KB_LOOP:
	MOV	AL,CS:[SI]
	INC	SI
	CALL	PRT_HEX
	LOOP	KB_LOOP
	POP	AX			; RECOVER WORK REGS
	CMP	AX,16			; FIRST PASS?
	JE	E20B
	POP	BX			; RESTORE REGS
	POP	CX
	POP	DX
	LOOP	E21			; LOOP TILL ALL MEM. CHECKED
	MOV	AL,10
	CALL	PRT_HEX 		; LINE FEED

;----- DMA TC0 SHOULD BE ON BY NOW - SEE IF IT IS

	IN	AL,DMA+08H
	AND	AL,00000001B		; TC0 STATUS BIT ON?
	JNZ	ROM_SCAN		; GO ON WITH NEXT TEST IF OK
	POP	DS
	MOV	MFG_ERR_FLAG,03H	; <><><><><><><><><><><><><>
	JMP	D6			; POST 101 ERROR MSG AND HALT

;----- PRINT FAILING ADDRESS AND XOR'ED PATTERN IF DATA COMPARE ERROR

E21A:	MOV	CH,AL			; SAVE FAILING BIT PATTERN
	MOV	AL,13			; CARRAGE RETURN
	CALL	PRT_HEX
	MOV	AL,10
	CALL	PRT_HEX
	POP	AX			; RECOVER AMT. OF GOOD MEMORY
	ADD	SP,6			; BALANCE STACK
	MOV	DX,DS			; GET FAILING SEGMENT
	POP	DS
	PUSH	DS
	MOV	MEMORY_SIZE,AX		; LOAD MEM. SIZE WORD TO SHOW
					; HOW MUCH MEM. WORKING
	MOV	MFG_ERR_FLAG,DH 	; <><><><><><><><><><><><><>
					; <><>CHECKPOINTS 08->A0<><>
	CALL	PRT_SEG 		; PRINT IT
	MOV	AL,CH			; GET FAILING BIT PATTERN
	CALL	XPC_BYTE		; CONVERT AND PRINT CODE
	MOV	SI,OFFSET E1		; SETUP ADDRESS OF ERROR MSG
	CALL	E_MSG			; PRINT ERROR MSG
;----------------------------------------------------------------
; CHECK FOR OPTIONAL ROM FROM C8000->F4000 IN 2K BLOCKS 	:
;	(A VALID MODULE HAS '55AA' IN THE FIRST 2 LOCATIONS,    :
;	LENGTH INDICATOR (LENGTH/512) IN THE 3D LOCATION AND	:
;	TEST/INIT. CODE STARTING IN THE 4TH LOCATION.)		:
;----------------------------------------------------------------
ROM_SCAN:
	MOV	DX,0C800H		; SET BEGINNING ADDRESS
ROM_SCAN_1:
	MOV	DS,DX
	SUB	BX,BX			; SET BX=0000
	MOV	AX,[BX] 		; GET 1ST WORD FROM MODULE
	PUSH	BX
	POP	BX			; BUS SETTLING
	CMP	AX,0AA55H		; = TO ID WORD?
	JNZ	NEXT_ROM		; PROCEED TO NEXT ROM IF NOT
	CALL	ROM_CHECK		; GO CHECK OUT MODULE
	JMP	ARE_WE_DONE		; CHECK FOR END OF ROM SPACE
NEXT_ROM:
	ADD	DX,0080H		; POINT TO NEXT 2K ADDRESS
ARE_WE_DONE:
	CMP	DX,0F600H		; AT F6000 YET?
	JL	ROM_SCAN_1		; GO CHECK ANOTHER ADD. IF NOT
	JMP	BASE_ROM_CHK		; GO CHECK BASIC ROM
;------------------------------------------------------------------------
; A CHECKSUM IS DONE FOR THE 4 ROS MODULES CONTAINING BASIC CODE	:
;------------------------------------------------------------------------
BASE_ROM_CHK:
	MOV	AH,4			; NO. OF ROS MODULES TO CHECK
E4:
	SUB	BX,BX			; SETUP STARTING ROS ADDR
	MOV	DS,DX
					; CHECK ROS
	CALL	ROS_CHECKSUM
	JE	E5			; CONTINUE IF OK
	CALL	ROM_ERR 		; POST ERROR
E5:
	ADD	DX,0200H		; POINT TO NEXT 8K MODULE
	DEC	AH			; ANY MORE TO DO?
	JNZ	E4			; YES - CONTINUE
;----------------------------------------------------------------
;	DISKETTE ATTACHMENT TEST				:
;DESCRIPTION							:
;	CHECK IF IPL DISKETTE DRIVE IS ATTACHED TO SYSTEM.  IF	:
;	ATTACHED, VERIFY STATUS OF NEC FDC AFTER A RESET. ISSUE :
;	A RECAL AND SEEK CMD TO FDC AND CHECK STATUS. COMPLETE	:
;	SYSTEM INITIALIZATION THEN PASS CONTROL TO THE BOOT	:
;	LOADER PROGRAM. 					:
;----------------------------------------------------------------
F9:
	POP	DS
	MOV	AL,BYTE PTR EQUIP_FLAG	; DISKETTE PRESENT?
	AND	AL,01H			; NO - BYPASS DISKETTE TEST
	JZ	F15
F10:					; DISK_TEST:
	IN	AL,INTA01
	AND	AL,0BFH 		; ENABLE DISKETTE INTERRUPTS
	OUT	INTA01,AL
	MOV	AH,0			; RESET NEC FDC
	MOV	DL,AH			; SET FOR DRIVE 0
	INT	13H			; VERIFY STATUS AFTER RESET
	TEST	AH,0FFH 		; STATUS OK?
	JNZ	F13			; NO - FDC FAILED

;----- TURN DRIVE 0 MOTOR ON

	MOV	DX,03F2H		; GET ADDR OF FDC CARD
	MOV	AL,1CH			; TURN MOTOR ON, EN DMA/INT
	OUT	DX,AL			; WRITE FDC CONTROL REG
	SUB	CX,CX
F11:					; MOTOR_WAIT:
	LOOP	F11			; WAIT FOR 1 SECOND
F12:					; MOTOR_WAIT1:
	LOOP	F12
	XOR	DX,DX			; SELECT DRIVE 0
	MOV	CH,1			; SELECT TRACK 1
	MOV	SEEK_STATUS,DL
	CALL	SEEK			; RECALIBRATE DISKETTE
	JC	F13			; GO TO ERR SUBROUTINE IF ERR
	MOV	CH,34			; SELECT TRACK 34
	CALL	SEEK			; SEEK TO TRACK 34
	JNC	F14			; OK, TURN MOTOR OFF
F13:					; DSK_ERR:
	MOV	SI,OFFSET F3		; GET ADDR OF MSG
	CALL	E_MSG			; GO PRINT ERROR MSG

;----- TURN DRIVE 0 MOTOR OFF

F14:					; DR0_OFF:
	MOV	AL,0CH			; TURN DRIVE 0 MOTOR OFF
	MOV	DX,03F2H		; FDC CTL ADDRESS
	OUT	DX,AL

;----- SETUP PRINTER AND RS232 BASE ADDRESSES IF DEVICE ATTACHED

F15:
	MOV	INTR_FLAG,00H		; SET STRAY INTERRUPT FLAG = 00
	MOV	SI,OFFSET KB_BUFFER	; SETUP KEYBOARD PARAMETERS
	MOV	BUFFER_HEAD,SI
	MOV	BUFFER_TAIL,SI
	MOV	BUFFER_START,SI
	ADD	SI,32			;DEFAULT BUFFER OF 32 BYTES
	MOV	BUFFER_END,SI
	MOV	DI,OFFSET PRINT_TIM_OUT ;SET DEFAULT PRINTER TIMEOUT
	PUSH	DS
	POP	ES
	MOV	AX,1414H		; DEFAULT=20
	STOSW
	STOSW
	MOV	AX,0101H		;RS232 DEFAULT=01
	STOSW
	STOSW
	IN	AL,INTA01
	AND	AL,0FCH 		; ENABLE TIMER AND KBD INTS
	OUT	INTA01,AL
	CMP	BP,0000H		; CHECK FOR BP= NON-ZERO
					; (ERROR HAPPENED)
	JE	F15A_0			; CONTINUE IF NO ERROR
	MOV	DX,2			; 2 SHORT BEEPS (ERROR)
	CALL	ERR_BEEP
	MOV	SI,OFFSET F3D		; LOAD ERROR MSG
	CALL	P_MSG
ERR_WAIT:
	MOV	AH,00
	INT	16H			; WAIT FOR 'F1' KEY
	CMP	AH,3BH
	JNE	ERR_WAIT
	JMP	F15A			; BYPASS ERROR
F15A_0:
	CMP	MFG_TST,1		; MFG MODE
	JE	F15A			; BYPASS BEEP
	MOV	DX,1			; 1 SHORT BEEP (NO ERRORS)
	CALL	ERR_BEEP
F15A:	MOV	AL,BYTE PTR EQUIP_FLAG	; GET SWITCHES
	AND	AL,00000001B		; 'LOOP POST' SWITCH ON
	JNZ	F15B			; CONTINUE WITH BRING-UP
	JMP	START
F15B:	SUB	AH,AH
	MOV	AL,CRT_MODE
	INT	10H			; CLEAR SCREEN
F15C:
	MOV	BP,OFFSET F4		; PRT_SRC_TBL
	MOV	SI,0
F16:					; PRT_BASE:
	MOV	DX,CS:[BP]		; GET PRINTER BASE ADDR
	MOV	AL,0AAH 		; WRITE DATA TO PORT A
	OUT	DX,AL
	PUSH	DS			; BUS SETTLEING
	IN	AL,DX			; READ PORT A
	POP	DS
	CMP	AL,0AAH 		; DATA PATTERN SAME
	JNE	F17			; NO - CHECK NEXT PRT CD
	MOV	PRINTER_BASE[SI],DX	; YES - STORE PRT BASE ADDR
	INC	SI			; INCREMENT TO NEXT WORD
	INC	SI
F17:
	INC	BP			; POINT TO NEXT BASE ADDR
	INC	BP
	CMP	BP,OFFSET F4E		; ALL POSSIBLE ADDRS CHECKED?
	JNE	F16			; PRT_BASE
	MOV	BX,0			; POINTER TO RS232 TABLE
	MOV	DX,3FAH 		; CHECK IF RS232 CD 1 ATTCH?
	IN	AL,DX			; READ INTR ID REG
	TEST	AL,0F8H
	JNZ	F18
	MOV	RS232_BASE[BX],3F8H	; SETUP RS232 CD #1 ADDR
	INC	BX
	INC	BX
F18:
	MOV	DX,2FAH 		; CHECK IF RS232 CD 2 ATTCH
	IN	AL,DX			; READ INTERRUPT ID REG
	TEST	AL,0F8H
	JNZ	F19			; BASE_END
	MOV	RS232_BASE[BX],2F8H	; SETUP RS232 CD #2
	INC	BX
	INC	BX

;----- SET UP EQUIP FLAG TO INDICATE NUMBER OF PRINTERS AND RS232 CARDS

F19:					; BASE_END:
	MOV	AX,SI			; SI HAS 2* NUMBER OF RS232
	MOV	CL,3			; SHIFT COUNT
	ROR	AL,CL			; ROTATE RIGHT 3 POSITIONS
	OR	AL,BL			; OR IN THE PRINTER COUNT
	MOV	BYTE PTR EQUIP_FLAG+1,AL	; STORE AS SECOND BYTE
	MOV	DX,201H
	IN	AL,DX
	NOP
	NOP
	NOP
	TEST	AL,0FH
	JNZ	F20			; NO_GAME_CARD
	OR	BYTE PTR EQUIP_FLAG+1,16
F20:					; NO_GAME_CARD:

;----- ENABLE NMI INTERRUPTS

	IN	AL,PORT_B		; RESET CHECK ENABLES
	OR	AL,30H
	OUT	PORT_B,AL
	AND	AL,0CFH
	OUT	PORT_B,AL
	MOV	AL,80H			; ENABLE NMI INTERRUPTS
	OUT	0A0H,AL
F21:					; LOAD_BOOT_STRAP:
	INT	19H			; GO TO THE BOOT LOADER

;----------------------------------------------------------------
; THIS SUBROUTINE PERFORMS A READ/WRITE STORAGE TEST ON A BLOCK :
;	OF STORAGE.						:
; ENTRY REQUIREMENTS:						:
;	ES = ADDRESS OF STORAGE SEGMENT BEING TESTED		:
;	DS = ADDRESS OF STORAGE SEGMENT BEING TESTED		:
;	CX = WORD COUNT OF STORAGE BLOCK TO BE TESTED		:
; EXIT PARAMETERS:						:
;	ZERO FLAG = 0 IF STORAGE ERROR (DATA COMPARE OR PARITY	:
;	CHECK.	AL=0 DENOTES A PARITY CHECK. ELSE AL=XOR'ED     :
;	BIT PATTERN OF THE EXPECTED DATA PATTERN VS THE ACTUAL	:
;	DATA READ.						:
; AX,BX,CX,DX,DI, AND SI ARE ALL DESTROYED.			:
;----------------------------------------------------------------

STGTST_CNT	PROC	NEAR
	CLD				; SET DIR FLAG TO INCREMENT
	SUB	DI,DI			; SET DI=OFFSET 0 REL TO ES REG
	SUB	AX,AX			; SETUP FOR 0->FF PATTERN TEST
C2_1:
	MOV	[DI],AL 		; ON FIRST BYTE
	MOV	AL,[DI]
	XOR	AL,AH			; O.K.?
	JNZ	C7			; GO ERROR IF NOT
	INC	AH
	MOV	AL,AH
	JNZ	C2_1			; LOOP TILL WRAP THROUGH FF
	MOV	BX,CX			; SAVE WORD COUNT OF BLOCK TO TEST
	SHL	BX,1			; CONVERT TO A BYTE COUNT
	MOV	AX,0AAAAH		; GET INITIAL DATA PATTERN TO WRITE
	MOV	DX,0FF55H		; SETUP OTHER DATA PATTERNS TO USE
	REP	STOSW			; FILL STORAGE LOCATIONS IN BLOCK
	IN	AL,PORT_B
	OR	AL,00110000B		; TOGGLE PARITY CHECK LATCHES
	OUT	PORT_B,AL
	NOP
	AND	AL,11001111B
	OUT	PORT_B,AL
C3:
	DEC	DI			; POINT TO LAST BYTE JUST WRITTEN
	STD				; SET DIR FLAG TO GO BACKWARDS
C4:
	MOV	SI,DI			; INITIALIZE DESTINATION POINTER
	MOV	CX,BX			; SETUP BYTE COUNT FOR LOOP
C5:					;	INNER TEST LOOP
	LODSB				; READ OLD TEST BYTE FROM STG [SI]+
	XOR	AL,AH			; DATA READ AS EXPECTED ?
	JNE	C7			; NO - GO TO ERROR ROUTINE
	MOV	AL,DL			; GET NEXT DATA PATTERN TO WRITE
	STOSB				; WRITE INTO LOC JUST READ [DI]+
	LOOP	C5			; DECREMENT BYTE COUNT AND LOOP   CX

	AND	AH,AH			; ENDING ZERO PATTERN WRITTEN TO STG ?
	JZ	C6X			; YES - RETURN TO CALLER WITH AL=0
	MOV	AH,AL			; SETUP NEW VALUE FOR COMPARE
	XCHG	DH,DL			; MOVE NEXT DATA PATTERN TO DL
	AND	AH,AH			; READING ZERO PATTERN THIS PASS ?
	JNZ	C6			; CONTINUE TEST SEQUENCE TILL ZERO DATA
	MOV	DL,AH			; ELSE SET ZERO FOR END READ PATTERN
	JMP	C3			; AND MAKE FINAL BACKWARDS PASS
C6:
	CLD				; SET DIR FLAG TO GO FORWARD
	INC	DI			; SET POINTER TO BEG LOCATION
	JZ	C4			; READ/WRITE FORWARD IN STG
	DEC	DI			; ADJUST POINTER
	MOV	DX,00001H		; SETUP 01 FOR PARITY BIT AND 00 FOR END
	JMP	C3			; READ/WRITE BACKWARD IN STG
C6X:
	IN	AL,PORT_C		; DID A PARITY ERROR OCCUR ?
	AND	AL,0C0H 		; ZERO FLAG WILL BE OFF PARITY ERROR
	MOV	AL,000H 		; AL=0 DATA COMPARE OK
C7:
	CLD				; SET DIRECTION FLAG TO INC
	RET
STGTST_CNT	ENDP
;------------------------------------------------------------------------
;	PRINT ADDRESS AND ERROR MESSAGE FOR ROM CHECKSUM ERRORS 	:
;------------------------------------------------------------------------
ROM_ERR PROC	NEAR
	PUSH	DX			; SAVE POINTER
	PUSH	AX
	MOV	DX,DS			; GET ADDRESS POINTER
	MOV	ES:MFG_ERR_FLAG,DH	; <><><><><><><><><><><><><><><>
					; <><><>CHECKPOINTS C0->F4<><><>
	CMP	DX,0C800H
	JL	ROM_ERR_BEEP		; SPECIAL ERROR INDICATION
	CALL	PRT_SEG 		; PRINT SEGEMENT IN ERROR
	MOV	SI,OFFSET F3A		; DISPLAY ERROR MSG
	CALL	E_MSG
ROM_ERR_END:
	POP	AX
	POP	DX
	RET
ROM_ERR_BEEP:
	MOV	DX,0102H		; BEEP 1 LONG, 2 SHORT
	CALL	ERR_BEEP
	JMP	SHORT ROM_ERR_END
ROM_ERR ENDP

;--- INT 19 ---------------------------------------------
; BOOT STRAP LOADER					:
;	TRACK 0, SECTOR 1 IS READ INTO THE		:
;	BOOT LOCATION (SEGMENT 0, OFFSET 7C00)		:
;	AND CONTROL IS TRANSFERRED THERE.		:
;							:
;	IF THERE IS A HARDWARE ERROR CONTROL IS 	:
;	TRANSFERRED TO THE ROM BASIC ENTRY POINT.	:
;--------------------------------------------------------
	ASSUME	CS:CODE,DS:ABS0
	ORG	0E6F2H

BOOT_STRAP	PROC	NEAR
	STI				; ENABLE INTERRUPTS
	SUB	AX,AX			; ESTABLISH ADDRESSING
	MOV	DS,AX

;----- RESET DISKETTE PARAMETER TABLE VECTOR

	MOV	WORD PTR DISK_POINTER,OFFSET DISK_BASE
	MOV	WORD PTR DISK_POINTER+2,CS

;----- LOAD SYSTEM FROM DISKETTE -- CX HAS RETRY COUNT

	MOV	CX,4			; SET RETRY COUNT
H1:					; IPL_SYSTEM
	PUSH	CX			; SAVE RETRY COUNT
	MOV	AH,0			; RESET THE DISKETTE SYSTEM
	INT	13H			; DISKETTE_IO
	JC	H2			; IF ERROR, TRY AGAIN
	MOV	AX,201H 		; READ IN THE SINGLE SECTOR
	SUB	DX,DX
	MOV	ES,DX
	MOV	BX,OFFSET BOOT_LOCN
					; DRIVE 0, HEAD 0
	MOV	CX,1			; SECTOR 1, TRACK 0
	INT	13H			; DISKETTE_IO
H2:
	POP	CX			; RECOVER RETRY COUNT
	JNC	H4			; CF SET BY UNSUCCESSFUL READ
	LOOP	H1			; DO IT FOR RETRY TIMES

;------ UNABLE TO IPL FROM THE DISKETTE

H3:
	INT	18H			; GO TO RESIDENT BASIC

;----- IPL WAS SUCCESSFUL

H4:
	JMP	BOOT_LOCN
BOOT_STRAP	ENDP

;-----INT 14-------------------------------------------------------------
; RS232_IO								:
;	THIS ROUTINE PROVIDES BYTE STREAM I/O TO THE COMMUNICATIONS	:
;	PORT ACCORDING TO THE PARAMETERS:				:
;	(AH)=0	INITIALIZE THE COMMUNICATIONS PORT			:
;		(AL) HAS PARAMETERS FOR INITIALIZATION			:
;									:
;	7	6	5	4	3	2	1	0	:
;	----- BAUD RATE --	-PARITY--     STOPBIT	--WORD LENGTH-- :
;	000 - 110		X0 - NONE	0 - 1	 10 - 7 BITS	:
;	001 - 150		01 - ODD	1 - 2	 11 - 8 BITS	:
;	010 - 300		11 - EVEN				:
;	011 - 600							:
;	100 - 1200							:
;	101 - 2400							:
;	110 - 4800							:
;	111 - 9600							:
;									:
;	ON RETURN, CONDITIONS SET AS IN CALL TO COMMO STATUS (AH=3)	:
;	(AH)=1	SEND THE CHARACTER IN (AL) OVER THE COMMO LINE		:
;		(AL) REGISTER IS PRESERVED				:
;		ON EXIT, BIT 7 OF AH IS SET IF THE ROUTINE WAS UNABLE	:
;			TO TRANSMIT THE BYTE OF DATA OVER THE LINE.	:
;			IF BIT 7 OF AH IS NOT SET, THE REMAINDER OF AH	:
;			IS SET AS IN A STATUS REQUEST, REFLECTING THE	:
;			CURRENT STATUS OF THE LINE.			:
;	(AH)=2	RECEIVE A CHARACTER IN (AL) FROM COMMO LINE BEFORE	:
;			RETURNING TO CALLER				:
;		ON EXIT, AH HAS THE CURRENT LINE STATUS, AS SET BY THE	:
;			THE STATUS ROUTINE, EXCEPT THAT THE ONLY BITS	:
;			LEFT ON ARE THE ERROR BITS (7,4,3,2,1)		:
;			IF AH HAS BIT 7 ON (TIME OUT) THE REMAINING	:
;			BITS ARE NOT PREDICTABLE.			:
;			THUS, AH IS NON ZERO ONLY WHEN AN ERROR 	:
;			OCCURRED.					:
;	(AH)=3	RETURN THE COMMO PORT STATUS IN (AX)			:
;		AH CONTAINS THE LINE CONTROL STATUS			:
;		BIT 7 = TIME OUT					:
;		BIT 6 = TRANS SHIFT REGISTER EMPTY			:
;		BIT 5 = TRAN HOLDING REGISTER EMPTY			:
;		BIT 4 = BREAK DETECT					:
;		BIT 3 = FRAMING ERROR					:
;		BIT 2 = PARITY ERROR					:
;		BIT 1 = OVERRUN ERROR					:
;		BIT 0 = DATA READY					:
;		AL CONTAINS THE MODEM STATUS				:
;		BIT 7 = RECEVED LINE SIGNAL DETECT			:
;		BIT 6 = RING INDICATOR					:
;		BIT 5 = DATA SET READY					:
;		BIT 4 = CLEAR TO SEND					:
;		BIT 3 = DELTA RECEIVE LINE SIGNAL DETECT		:
;		BIT 2 = TRAILING EDGE RING DETECTOR			:
;		BIT 1 = DELTA DATA SET READY				:
;		BIT 0 = DELTA CLEAR TO SEND				:
;									:
;	(DX) = PARAMETER INDICATING WHICH RS232 CARD (0,1 ALLOWED)	:
;									:
; DATA AREA RS232_BASE CONTAINS THE BASE ADDRESS OF THE 8250 ON THE	:
;	CARD LOCATION 400H CONTAINS UP TO 4 RS232 ADDRESSES POSSIBLE	:
;	DATA AREA LABEL RS232_TIM_OUT (BYTE) CONTAINS OUTER LOOP COUNT	:
;	VALUE FOR TIMEOUT (DEFAULT=1)					:
; OUTPUT								:
;	AX MODIFIED ACCORDING TO PARMS OF CALL				:
;	ALL OTHERS UNCHANGED						:
;------------------------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA
	ORG	0E729H
A1	LABEL	WORD			; TABLE OF INIT VALUE
	DW	1047			; 110 BAUD
	DW	768			; 150
	DW	384			; 300
	DW	192			; 600
	DW	96			; 1200
	DW	48			; 2400
	DW	24			; 4800
	DW	12			; 9600

RS232_IO	PROC	FAR

;----- VECTOR TO APPROPRIATE ROUTINE

	STI				; INTERRUPTS BACK ON
	PUSH	DS			; SAVE SEGMENT
	PUSH	DX
	PUSH	SI
	PUSH	DI
	PUSH	CX
	PUSH	BX
	MOV	SI,DX			; RS232 VALUE TO SI
	MOV	DI,DX
	SHL	SI,1			; WORD OFFSET
	CALL	DDS
	MOV	DX,RS232_BASE[SI]	; GET BASE ADDRESS
	OR	DX,DX			; TEST FOR 0 BASE ADDRESS
	JZ	A3			; RETURN
	OR	AH,AH			; TEST FOR (AH)=0
	JZ	A4			; COMMUN INIT
	DEC	AH			; TEST FOR (AH)=1
	JZ	A5			; SEND AL
	DEC	AH			; TEST FOR (AH)=2
	JZ	A12			; RECEIVE INTO AL
A2:
	DEC	AH			; TEST FOR (AH)=3
	JNZ	A3
	JMP	A18			; COMMUNICATION STATUS
A3:					; RETURN FROM RS232
	POP	BX
	POP	CX
	POP	DI
	POP	SI
	POP	DX
	POP	DS
	IRET				; RETURN TO CALLER, NO ACTION

;----- INITIALIZE THE COMMUNICATIONS PORT

A4:
	MOV	AH,AL			; SAVE INIT PARMS IN AH
	ADD	DX,3			; POINT TO 8250 CONTROL REGISTER
	MOV	AL,80H
	OUT	DX,AL			; SET DLAB=1

;----- DETERMINE BAUD RATE DIVISOR

	MOV	DL,AH			; GET PARMS TO DL
	MOV	CL,4
	ROL	DL,CL
	AND	DX,0EH			; ISOLATE THEM
	MOV	DI,OFFSET A1		; BASE OF TABLE
	ADD	DI,DX			; PUT INTO INDEX REGISTER
	MOV	DX,RS232_BASE[SI]	; POINT TO HIGH ORDER OF DIVISOR
	INC	DX
	MOV	AL,CS:[DI]+1		; GET HIGH ORDER OF DIVISOR
	OUT	DX,AL			; SET MS OF DIV TO 0
	DEC	DX
	MOV	AL,CS:[DI]		; GET LOW ORDER OF DIVISOR
	OUT	DX,AL			; SET LOW OF DIVISOR
	ADD	DX,3
	MOV	AL,AH			; GET PARMS BACK
	AND	AL,01FH 		; STRIP OFF THE BAUD BITS
	OUT	DX,AL			; LINE CONTROL TO 8 BITS
	DEC	DX
	DEC	DX
	MOV	AL,0
	OUT	DX,AL			; INTERRUPT ENABLES ALL OFF
	JMP	SHORT A18		; COM_STATUS

;----- SEND CHARACTER IN (AL) OVER COMMO LINE

A5:
	PUSH	AX			; SAVE CHAR TO SEND
	ADD	DX,4			; MODEM CONTROL REGISTER
	MOV	AL,3			; DTR AND RTS
	OUT	DX,AL			; DATA TERMINAL READY, REQUEST TO SEND
	INC	DX			; MODEM STATUS REGISTER
	INC	DX
	MOV	BH,30H			; DATA SET READY & CLEAR TO SEND
	CALL	WAIT_FOR_STATUS 	; ARE BOTH TRUE
	JE	A9			; YES, READY TO TRANSMIT CHAR
A7:
	POP	CX
	MOV	AL,CL			; RELOAD DATA BYTE
A8:
	OR	AH,80H			; INDICATE TIME OUT
	JMP	A3			; RETURN
A9:					; CLEAR_TO_SEND
	DEC	DX			; LINE STATUS REGISTER
A10:					; WAIT_SEND
	MOV	BH,20H			; IS TRANSMITTER READY
	CALL	WAIT_FOR_STATUS 	; TEST FOR TRANSMITTER READY
	JNZ	A7			; RETURN WITH TIME OUT SET
A11:					; OUT_CHAR
	SUB	DX,5			; DATA PORT
	POP	CX			; RECOVER IN CX TEMPORARILY
	MOV	AL,CL			; MOVE CHAR TO AL FOR OUT, STATUS IN AH
	OUT	DX,AL			; OUTPUT CHARACTER
	JMP	A3			; RETURN

;----- RECEIVE CHARACTER FROM COMMO LINE

A12:
	ADD	DX,4			; MODEM CONTROL REGISTER
	MOV	AL,1			; DATA TERMINAL READY
	OUT	DX,AL
	INC	DX			; MODEM STATUS REGISTER
	INC	DX
A13:					; WAIT_DSR
	MOV	BH,20H			; DATA SET READY
	CALL	WAIT_FOR_STATUS 	; TEST FOR DSR
	JNZ	A8			; RETURN WITH ERROR
A15:					; WAIT_DSR_END
	DEC	DX			; LINE STATUS REGISTER
A16:					; WAIT_RECV
	MOV	BH,1			; RECEIVE BUFFER FULL
	CALL	WAIT_FOR_STATUS 	; TEST FOR REC. BUFF. FULL
	JNZ	A8			; SET TIME OUT ERROR
A17:					; GET_CHAR
	AND	AH,00011110B		; TEST FOR ERROR CONDITIONS ON RECV CHAR
	MOV	DX,RS232_BASE[SI]	; DATA PORT
	IN	AL,DX			; GET CHARACTER FROM LINE
	JMP	A3			; RETURN

;----- COMMO PORT STATUS ROUTINE

A18:
	MOV	DX,RS232_BASE[SI]
	ADD	DX,5			; CONTROL PORT
	IN	AL,DX			; GET LINE CONTROL STATUS
	MOV	AH,AL			; PUT IN AH FOR RETURN
	INC	DX			; POINT TO MODEM STATUS REGISTER
	IN	AL,DX			; GET MODEM CONTROL STATUS
	JMP	A3			; RETURN
;----------------------------------------
; WAIT FOR STATUS ROUTINE		:
;					:
; ENTRY:				:
;	BH=STATUS BIT(S) TO LOOK FOR,	:
;	DX=ADDR. OF STATUS REG		:
; EXIT: 				:
;	ZERO FLAG ON  = STATUS FOUND	:
;	ZERO FLAG OFF = TIMEOUT.	:
;	AH=LAST STATUS READ		:
;----------------------------------------
WAIT_FOR_STATUS PROC	NEAR
	MOV	BL,RS232_TIM_OUT[DI]	; LOAD OUTER LOOP COUNT
WFS0:
	SUB	CX,CX
WFS1:
	IN	AL,DX			; GET STATUS
	MOV	AH,AL			; MOVE TO AH
	AND	AL,BH			; ISOLATE BITS TO TEST
	CMP	AL,BH			; EXACTLY = TO MASK
	JE	WFS_END 		; RETURN WITH ZERO FLAG ON
	LOOP	WFS1			; TRY AGAIN
	DEC	BL
	JNZ	WFS0

	OR	BH,BH			; SET ZERO FLAG OFF
WFS_END:
	RET
WAIT_FOR_STATUS ENDP
RS232_IO	ENDP

F3D	DB	'ERROR. (RESUME = "F1" KEY)',13,10      ; ERROR PROMPT

;---- INT 16 ------------------------------------------------------------
; KEYBOARD I/O								:
;	THESE ROUTINES PROVIDE KEYBOARD SUPPORT 			:
; INPUT 								:
;	(AH)=0	READ THE NEXT ASCII CHARACTER STRUCK FROM THE KEYBOARD	:
;		RETURN THE RESULT IN (AL), SCAN CODE IN (AH)		:
;	(AH)=1	SET THE Z FLAG TO INDICATE IF AN ASCII CHARACTER IS	:
;		AVAILABLE TO BE READ.					:
;		(ZF)=1 -- NO CODE AVAILABLE				:
;		(ZF)=0 -- CODE IS AVAILABLE				:
;		IF ZF = 0, THE NEXT CHARACTER IN THE BUFFER TO BE READ	:
;		IS IN AX, AND THE ENTRY REMAINS IN THE BUFFER		:
;	(AH)=2	RETURN THE CURRENT SHIFT STATUS IN AL REGISTER		:
;		THE BIT SETTINGS FOR THIS CODE ARE INDICATED IN THE	:
;		THE EQUATES FOR KB_FLAG 				:
; OUTPUT								:
;	AS NOTED ABOVE, ONLY AX AND FLAGS CHANGED			:
;	ALL REGISTERS PRESERVED 					:
;------------------------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA
	ORG	0E82EH
KEYBOARD_IO	PROC	FAR
	STI				; INTERRUPTS BACK ON
	PUSH	DS			; SAVE CURRENT DS
	PUSH	BX			; SAVE BX TEMPORARILY
	CALL	DDS
	OR	AH,AH			; AH=0
	JZ	K1			; ASCII_READ
	DEC	AH			; AH=1
	JZ	K2			; ASCII_STATUS
	DEC	AH			; AH=2
	JZ	K3			; SHIFT_STATUS
	JMP	SHORT INT10_END 	; EXIT

;----- READ THE KEY TO FIGURE OUT WHAT TO DO

K1:					; ASCII READ
	STI				; INTERRUPTS BACK ON DURING LOOP
	NOP				; ALLOW AN INTERRUPT TO OCCUR
	CLI				; INTERRUPTS BACK OFF
	MOV	BX,BUFFER_HEAD		; GET POINTER TO HEAD OF BUFFER
	CMP	BX,BUFFER_TAIL		; TEST END OF BUFFER
	JZ	K1			; LOOP UNTIL SOMETHING IN BUFFER
	MOV	AX,[BX] 		; GET SCAN CODE AND ASCII CODE
	CALL	K4			; MOVE POINTER TO NEXT POSITION
	MOV	BUFFER_HEAD,BX		; STORE VALUE IN VARIABLE
	JMP	SHORT INT10_END 	; RETURN

;------ ASCII STATUS

K2:
	CLI				; INTERRUPTS OFF
	MOV	BX,BUFFER_HEAD		; GET HEAD POINTER
	CMP	BX,BUFFER_TAIL		; IF EQUAL (Z=1) THEN NOTHING HERE
	MOV	AX,[BX]
	STI				; INTERRUPTS BACK ON
	POP	BX			; RECOVER REGISTER
	POP	DS			; RECOVER SEGMENT
	RET	2			; THROW AWAY FLAGS

;------ SHIFT STATUS

K3:
	MOV	AL,KB_FLAG		; GET THE SHIFT STATUS FLAGS
INT10_END:
	POP	BX			; RECOVER REGISTER
	POP	DS			; RECOVER REGISTERS
	IRET				; RETURN TO CALLER
KEYBOARD_IO	ENDP

;----- INCREMENT A BUFFER POINTER

K4	PROC	NEAR
	INC	BX			; MOVE TO NEXT WORD IN LIST
	INC	BX
	CMP	BX,BUFFER_END		; AT END OF BUFFER?
	JNE	K5			; NO, CONTINUE
	MOV	BX,BUFFER_START 	; YES, RESET TO BUFFER BEGINNING
K5:
	RET
K4	ENDP

;----- TABLE OF SHIFT KEYS AND MASK VALUES

K6	LABEL	BYTE
	DB	INS_KEY 		; INSERT KEY
	DB	CAPS_KEY,NUM_KEY,SCROLL_KEY,ALT_KEY,CTL_KEY
	DB	LEFT_KEY,RIGHT_KEY
K6L	EQU	$-K6

;----- SHIFT_MASK_TABLE

K7	LABEL	BYTE
	DB	INS_SHIFT		; INSERT MODE SHIFT
	DB	CAPS_SHIFT,NUM_SHIFT,SCROLL_SHIFT,ALT_SHIFT,CTL_SHIFT
	DB	LEFT_SHIFT,RIGHT_SHIFT

;----- SCAN CODE TABLES

K8		DB	27,-1,0,-1,-1,-1,30,-1
		DB	-1,-1,-1,31,-1,127,-1,17
		DB	23,5,18,20,25,21,9,15
		DB	16,27,29,10,-1,1,19
		DB	4,6,7,8,10,11,12,-1,-1
		DB	-1,-1,28,26,24,3,22,2
		DB	14,13,-1,-1,-1,-1,-1,-1
		DB	' ',-1
;----- CTL TABLE SCAN
K9	LABEL	BYTE
		DB	94,95,96,97,98,99,100,101
		DB	102,103,-1,-1,119,-1,132,-1
		DB	115,-1,116,-1,117,-1,118,-1
		DB	-1
;----- LC TABLE
K10	LABEL	BYTE
		DB	01BH,'1234567890-=',08H,09H
		DB	'qwertyuiop[]',0DH,-1,'asdfghjkl;',027H
		DB	60H,-1,5CH,'zxcvbnm,./',-1,'*',-1,' '
		DB	-1
;----- UC TABLE
K11	LABEL	BYTE
		DB	27,'!@#$',37,05EH,'&*()_+',08H,0
		DB	'QWERTYUIOP{}',0DH,-1,'ASDFGHJKL:"'
		DB	07EH,-1,'|ZXCVBNM<>?',-1,0,-1,' ',-1
;----- UC TABLE SCAN
K12	LABEL	BYTE
		DB	84,85,86,87,88,89,90
		DB	91,92,93
;----- ALT TABLE SCAN
K13	LABEL	BYTE
		DB	104,105,106,107,108
		DB	109,110,111,112,113
;----- NUM STATE TABLE
K14	LABEL	BYTE
		DB	'789-456+1230.'
;----- BASE CASE TABLE
K15	LABEL	BYTE
		DB	71,72,73,-1,75,-1,77
		DB	-1,79,80,81,82,83

;----- KEYBOARD INTERRUPT ROUTINE

	ORG	0E987H
KB_INT	PROC	FAR
	STI				; ALLOW FURTHER INTERRUPTS
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	SI
	PUSH	DI
	PUSH	DS
	PUSH	ES
	CLD				; FORWARD DIRECTION
	CALL	DDS
	IN	AL,KB_DATA		; READ IN THE CHARACTER
	PUSH	AX			; SAVE IT
	IN	AL,KB_CTL		; GET THE CONTROL PORT
	MOV	AH,AL			; SAVE VALUE
	OR	AL,80H			; RESET BIT FOR KEYBOARD
	OUT	KB_CTL,AL
	XCHG	AH,AL			; GET BACK ORIGINAL CONTROL
	OUT	KB_CTL,AL		; KB HAS BEEN RESET
	POP	AX			; RECOVER SCAN CODE
	MOV	AH,AL			; SAVE SCAN CODE IN AH ALSO

;----- TEST FOR OVERRUN SCAN CODE FROM KEYBOARD

	CMP	AL,0FFH 		; IS THIS AN OVERRUN CHAR
	JNZ	K16			; NO, TEST FOR SHIFT KEY
	JMP	K62			; BUFFER_FULL_BEEP

;----- TEST FOR SHIFT KEYS

K16:					; TEST_SHIFT
	AND	AL,07FH 		; TURN OFF THE BREAK BIT
	PUSH	CS
	POP	ES			; ESTABLISH ADDRESS OF SHIFT TABLE
	MOV	DI,OFFSET K6		; SHIFT KEY TABLE
	MOV	CX,K6L			; LENGTH
	REPNE	SCASB			; LOOK THROUGH THE TABLE FOR A MATCH
	MOV	AL,AH			; RECOVER SCAN CODE
	JE	K17			; JUMP IF MATCH FOUND
	JMP	K25			; IF NO MATCH, THEN SHIFT NOT FOUND

;----- SHIFT KEY FOUND

K17:	SUB	DI,OFFSET K6+1		; ADJUST PTR TO SCAN CODE MTCH
	MOV	AH,CS:K7[DI]		; GET MASK INTO AH
	TEST	AL,80H			; TEST FOR BREAK KEY
	JNZ	K23			; BREAK_SHIFT_FOUND

;----- SHIFT MAKE FOUND, DETERMINE SET OR TOGGLE

	CMP	AH,SCROLL_SHIFT
	JAE	K18			; IF SCROLL SHIFT OR ABOVE, TOGGLE KEY

;----- PLAIN SHIFT KEY, SET SHIFT ON

	OR	KB_FLAG,AH		; TURN ON SHIFT BIT
	JMP	K26			; INTERRUPT_RETURN

;----- TOGGLED SHIFT KEY, TEST FOR 1ST MAKE OR NOT

K18:					; SHIFT-TOGGLE
	TEST	KB_FLAG, CTL_SHIFT	; CHECK CTL SHIFT STATE
	JNZ	K25			; JUMP IF CTL STATE
	CMP	AL, INS_KEY		; CHECK FOR INSERT KEY
	JNZ	K22			; JUMP IF NOT INSERT KEY
	TEST	KB_FLAG, ALT_SHIFT	; CHECK FOR ALTERNATE SHIFT
	JNZ	K25			; JUMP IF ALTERNATE SHIFT
K19:	TEST	KB_FLAG, NUM_STATE	; CHECK FOR BASE STATE
	JNZ	K21			; JUMP IF NUM LOCK IS ON
	TEST	KB_FLAG, LEFT_SHIFT+RIGHT_SHIFT
	JZ	K22			; JUMP IF BASE STATE

K20:					; NUMERIC ZERO, NOT INSERT KEY
	MOV	AX, 5230H		; PUT OUT AN ASCII ZERO
	JMP	K57			; BUFFER_FILL
K21:					; MIGHT BE NUMERIC
	TEST	KB_FLAG, LEFT_SHIFT+RIGHT_SHIFT
	JZ	K20			; JUMP NUMERIC, NOT INSERT

K22:					; SHIFT TOGGLE KEY HIT; PROCESS IT
	TEST	AH,KB_FLAG_1		; IS KEY ALREADY DEPRESSED
	JNZ	K26			; JUMP IF KEY ALREADY DEPRESSED
	OR	KB_FLAG_1,AH		; INDICATE THAT THE KEY IS DEPRESSED
	XOR	KB_FLAG,AH		; TOGGLE THE SHIFT STATE
	CMP	AL,INS_KEY		; TEST FOR 1ST MAKE OF INSERT KEY
	JNE	K26			; JUMP IF NOT INSERT KEY
	MOV	AX,INS_KEY*256		; SET SCAN CODE INTO AH, 0 INTO AL
	JMP	K57			; PUT INTO OUTPUT BUFFER

;----- BREAK SHIFT FOUND

K23:					; BREAK-SHIFT-FOUND
	CMP	AH,SCROLL_SHIFT 	; IS THIS A TOGGLE KEY
	JAE	K24			; YES, HANDLE BREAK TOGGLE
	NOT	AH			; INVERT MASK
	AND	KB_FLAG,AH		; TURN OFF SHIFT BIT
	CMP	AL,ALT_KEY+80H		; IS THIS ALTERNATE SHIFT RELEASE
	JNE	K26			; INTERRUPT_RETURN

;----- ALTERNATE SHIFT KEY RELEASED, GET THE VALUE INTO BUFFER

	MOV	AL,ALT_INPUT
	MOV	AH,0			; SCAN CODE OF 0
	MOV	ALT_INPUT,AH		; ZERO OUT THE FIELD
	CMP	AL,0			; WAS THE INPUT=0
	JE	K26			; INTERRUPT_RETURN
	JMP	K58			; IT WASN'T, SO PUT IN BUFFER
K24:					; BREAK-TOGGLE
	NOT	AH			; INVERT MASK
	AND	KB_FLAG_1,AH		; INDICATE NO LONGER DEPRESSED
	JMP	SHORT K26		; INTERRUPT_RETURN

;----- TEST FOR HOLD STATE

K25:					; NO-SHIFT-FOUND
	CMP	AL,80H			; TEST FOR BREAK KEY
	JAE	K26			; NOTHING FOR BREAK CHARS FROM HERE ON
	TEST	KB_FLAG_1,HOLD_STATE	; ARE WE IN HOLD STATE
	JZ	K28			; BRANCH AROUND TEST IF NOT
	CMP	AL,NUM_KEY
	JE	K26			; CAN'T END HOLD ON NUM_LOCK
	AND	KB_FLAG_1,NOT HOLD_STATE	; TURN OFF THE HOLD STATE BIT
K26:					; INTERRUPT-RETURN
	CLI				; TURN OFF INTERRUPTS
	MOV	AL,EOI			; END OF INTERRUPT COMMAND
	OUT	020H,AL 		; SEND COMMAND TO INTERRUPT CONTROL PORT
K27:					; INTERRUPT-RETURN-NO-EOI
	POP	ES
	POP	DS
	POP	DI
	POP	SI
	POP	DX
	POP	CX
	POP	BX
	POP	AX			; RESTORE STATE
	IRET				; RETURN, INTERRUPTS BACK ON
					;  WITH FLAG CHANGE

;----- NOT IN HOLD STATE, TEST FOR SPECIAL CHARS

K28:					; NO-HOLD-STATE
	TEST	KB_FLAG,ALT_SHIFT	; ARE WE IN ALTERNATE SHIFT
	JNZ	K29			; JUMP IF ALTERNATE SHIFT
	JMP	K38			; JUMP IF NOT ALTERNATE

;----- TEST FOR RESET KEY SENTENCE (CTL ALT DEL)

K29:					; TEST-RESET
	TEST	KB_FLAG,CTL_SHIFT	; ARE WE IN CONTROL SHIFT ALSO
	JZ	K31			; NO_RESET
	CMP	AL,DEL_KEY		; SHIFT STATE IS THERE, TEST KEY
	JNE	K31			; NO_RESET

;----- CTL-ALT-DEL HAS BEEN FOUND, DO I/O CLEANUP

	MOV	RESET_FLAG, 1234H	; SET FLAG FOR RESET FUNCTION
	JMP	RESET			; JUMP TO POWER ON DIAGNOSTICS

;----- ALT-INPUT-TABLE
K30	LABEL	BYTE
	DB	82,79,80,81,75,76,77
	DB	71,72,73		; 10 NUMBERS ON KEYPAD
;----- SUPER-SHIFT-TABLE
	DB	16,17,18,19,20,21,22,23 ; A-Z TYPEWRITER CHARS
	DB	24,25,30,31,32,33,34,35
	DB	36,37,38,44,45,46,47,48
	DB	49,50

;----- IN ALTERNATE SHIFT, RESET NOT FOUND

K31:					; NO-RESET
	CMP	AL,57			; TEST FOR SPACE KEY
	JNE	K32			; NOT THERE
	MOV	AL,' '                  ; SET SPACE CHAR
	JMP	K57			; BUFFER_FILL

;----- LOOK FOR KEY PAD ENTRY

K32:					; ALT-KEY-PAD
	MOV	DI,OFFSET K30		; ALT-INPUT-TABLE
	MOV	CX,10			; LOOK FOR ENTRY USING KEYPAD
	REPNE	SCASB			; LOOK FOR MATCH
	JNE	K33			; NO_ALT_KEYPAD
	SUB	DI,OFFSET K30+1 	; DI NOW HAS ENTRY VALUE
	MOV	AL,ALT_INPUT		; GET THE CURRENT BYTE
	MOV	AH,10			; MULTIPLY BY 10
	MUL	AH
	ADD	AX,DI			; ADD IN THE LATEST ENTRY
	MOV	ALT_INPUT,AL		; STORE IT AWAY
	JMP	K26			; THROW AWAY THAT KEYSTROKE

;----- LOOK FOR SUPERSHIFT ENTRY

K33:					; NO-ALT-KEYPAD
	MOV	ALT_INPUT,0		; ZERO ANY PREVIOUS ENTRY INTO INPUT
	MOV	CX,26			; DI,ES ALREADY POINTING
	REPNE	SCASB			; LOOK FOR MATCH IN ALPHABET
	JNE	K34			; NOT FOUND, FUNCTION KEY OR OTHER
	MOV	AL,0			; ASCII CODE OF ZERO
	JMP	K57			; PUT IT IN THE BUFFER

;----- LOOK FOR TOP ROW OF ALTERNATE SHIFT

K34:					; ALT-TOP-ROW
	CMP	AL,2			; KEY WITH '1' ON IT
	JB	K35			; NOT ONE OF INTERESTING KEYS
	CMP	AL,14			; IS IT IN THE REGION
	JAE	K35			; ALT-FUNCTION
	ADD	AH,118			; CONVERT PSUEDO SCAN CODE TO RANGE
	MOV	AL,0			; INDICATE AS SUCH
	JMP	K57			; BUFFER_FILL

;----- TRANSLATE ALTERNATE SHIFT PSEUDO SCAN CODES

K35:					; ALT-FUNCTION
	CMP	AL,59			; TEST FOR IN TABLE
	JAE	K37			; ALT-CONTINUE
K36:					; CLOSE-RETURN
	JMP	K26			; IGNORE THE KEY
K37:					; ALT-CONTINUE
	CMP	AL,71			; IN KEYPAD REGION
	JAE	K36			; IF SO, IGNORE
	MOV	BX,OFFSET K13		; ALT SHIFT PSEUDO SCAN TABLE
	JMP	K63			; TRANSLATE THAT

;----- NOT IN ALTERNATE SHIFT

K38:					; NOT-ALT-SHIFT
	TEST	KB_FLAG,CTL_SHIFT	; ARE WE IN CONTROL SHIFT
	JZ	K44			; NOT-CTL-SHIFT

;----- CONTROL SHIFT, TEST SPECIAL CHARACTERS
;----- TEST FOR BREAK AND PAUSE KEYS

	CMP	AL,SCROLL_KEY		; TEST FOR BREAK
	JNE	K39			; NO-BREAK
	MOV	BX,BUFFER_START 	; RESET BUFFER TO EMPTY
	MOV	BUFFER_HEAD,BX		;
	MOV	BUFFER_TAIL,BX		;
	MOV	BIOS_BREAK,80H		; TURN ON BIOS_BREAK BIT
	INT	1BH			; BREAK INTERRUPT VECTOR
	SUB	AX,AX			; PUT OUT DUMMY CHARACTER
	JMP	K57			; BUFFER_FILL
K39:					; NO-BREAK
	CMP	AL,NUM_KEY		; LOOK FOR PAUSE KEY
	JNE	K41			; NO-PAUSE
	OR	KB_FLAG_1,HOLD_STATE	; TURN ON THE HOLD FLAG
	MOV	AL,EOI			; END OF INTERRUPT TO CONTROL PORT
	OUT	020H,AL 		; ALLOW FURTHER KEYSTROKE INTS

;----- DURING PAUSE INTERVAL, TURN CRT BACK ON

	CMP	CRT_MODE,7		; IS THIS BLACK AND WHITE CARD
	JE	K40			; YES, NOTHING TO DO
	MOV	DX,03D8H		; PORT FOR COLOR CARD
	MOV	AL,CRT_MODE_SET 	; GET THE VALUE OF THE CURRENT MODE
	OUT	DX,AL			; SET THE CRT MODE, SO THAT CRT IS ON
K40:					; PAUSE-LOOP
	TEST	KB_FLAG_1,HOLD_STATE
	JNZ	K40			; LOOP UNTIL FLAG TURNED OFF
	JMP	K27			; INTERRUPT_RETURN_NO_EOI
K41:					; NO-PAUSE

;----- TEST SPECIAL CASE KEY 55

	CMP	AL,55
	JNE	K42			; NOT-KEY-55
	MOV	AX,114*256		; START/STOP PRINTING SWITCH
	JMP	K57			; BUFFER_FILL

;----- SET UP TO TRANSLATE CONTROL SHIFT

K42:					; NOT-KEY-55
	MOV	BX,OFFSET K8		; SET UP TO TRANSLATE CTL
	CMP	AL,59			; IS IT IN TABLE
					; CTL-TABLE-TRANSLATE
	JB	K56			; YES, GO TRANSLATE CHAR
K43:					; CTL-TABLE-TRANSLATE
	MOV	BX,OFFSET K9		; CTL TABLE SCAN
	JMP	K63			; TRANSLATE_SCAN

;----- NOT IN CONTROL SHIFT

K44:					; NOT-CTL-SHIFT
	CMP	AL,71			; TEST FOR KEYPAD REGION
	JAE	K48			; HANDLE KEYPAD REGION
	TEST	KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT
	JZ	K54			; TEST FOR SHIFT STATE

;----- UPPER CASE, HANDLE SPECIAL CASES

	CMP	AL,15			; BACK TAB KEY
	JNE	K45			; NOT-BACK-TAB
	MOV	AX,15*256		; SET PSEUDO SCAN CODE
	JMP	SHORT K57		; BUFFER_FILL
K45:					; NOT-BACK-TAB
	CMP	AL,55			; PRINT SCREEN KEY
	JNE	K46			; NOT-PRINT-SCREEN

;----- ISSUE INTERRUPT TO INDICATE PRINT SCREEN FUNCTION

	MOV	AL,EOI			; END OF CURRENT INTERRUPT
	OUT	020H,AL 		;  SO FURTHER THINGS CAN HAPPEN
	INT	5H			; ISSUE PRINT SCREEN INTERRUPT
	JMP	K27			; GO BACK WITHOUT EOI OCCURRING
K46:					; NOT-PRINT-SCREEN
	CMP	AL,59			; FUNCTION KEYS
	JB	K47			; NOT-UPPER-FUNCTION
	MOV	BX,OFFSET K12		; UPPER CASE PSEUDO SCAN CODES
	JMP	K63			; TRANSLATE_SCAN
K47:					; NOT-UPPER-FUNCTION
	MOV	BX,OFFSET K11		; POINT TO UPPER CASE TABLE
	JMP	SHORT K56		; OK, TRANSLATE THE CHAR

;----- KEYPAD KEYS, MUST TEST NUM LOCK FOR DETERMINATION

K48:					; KEYPAD-REGION
	TEST	KB_FLAG,NUM_STATE	; ARE WE IN NUM_LOCK
	JNZ	K52			; TEST FOR SURE
	TEST	KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT	; ARE WE IN SHIFT STATE
	JNZ	K53			; IF SHIFTED, REALLY NUM STATE

;----- BASE CASE FOR KEYPAD

K49:					; BASE-CASE
	CMP	AL,74			; SPECIAL CASE FOR A COUPLE OF KEYS
	JE	K50			; MINUS
	CMP	AL,78
	JE	K51
	SUB	AL,71			; CONVERT ORIGIN
	MOV	BX,OFFSET K15		; BASE CASE TABLE
	JMP	SHORT K64		; CONVERT TO PSEUDO SCAN
K50:
	MOV	AX,74*256+'-'           ; MINUS
	JMP	SHORT K57		; BUFFER_FILL
K51:
	MOV	AX,78*256+'+'           ; PLUS
	JMP	SHORT K57		; BUFFER_FILL

;----- MIGHT BE NUM LOCK, TEST SHIFT STATUS

K52:					; ALMOST-NUM-STATE
	TEST	KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT
	JNZ	K49			; SHIFTED TEMP OUT OF NUM STATE
K53:					; REALLY_NUM_STATE
	SUB	AL,70			; CONVERT ORIGIN
	MOV	BX,OFFSET K14		; NUM STATE TABLE
	JMP	SHORT K56		; TRANSLATE_CHAR

;----- PLAIN OLD LOWER CASE

K54:					; NOT-SHIFT
	CMP	AL,59			; TEST FOR FUNCTION KEYS
	JB	K55			; NOT-LOWER-FUNCTION
	MOV	AL,0			; SCAN CODE IN AH ALREADY
	JMP	SHORT K57		; BUFFER_FILL
K55:					; NOT-LOWER-FUNCTION
	MOV	BX,OFFSET K10		; LC TABLE

;----- TRANSLATE THE CHARACTER

K56:					; TRANSLATE-CHAR
	DEC	AL			; CONVERT ORIGIN
	XLAT	CS:K11			; CONVERT THE SCAN CODE TO ASCII

;----- PUT CHARACTER INTO BUFFER

K57:					; BUFFER-FILL
	CMP	AL,-1			; IS THIS AN IGNORE CHAR
	JE	K59			; YES, DO NOTHING WITH IT
	CMP	AH,-1			; LOOK FOR -1 PSEUDO SCAN
	JE	K59			; NEAR_INTERRUPT_RETURN

;----- HANDLE THE CAPS LOCK PROBLEM

K58:					; BUFFER-FILL-NOTEST
	TEST	KB_FLAG,CAPS_STATE	; ARE WE IN CAPS LOCK STATE
	JZ	K61			; SKIP IF NOT

;----- IN CAPS LOCK STATE

	TEST	KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT	; TEST FOR SHIFT STATE
	JZ	K60			; IF NOT SHIFT, CONVERT LOWER TO UPPER

;----- CONVERT ANY UPPER CASE TO LOWER CASE

	CMP	AL,'A'                  ; FIND OUT IF ALPHABETIC
	JB	K61			; NOT_CAPS_STATE
	CMP	AL,'Z'
	JA	K61			; NOT_CAPS_STATE
	ADD	AL,'a'-'A'              ; CONVERT TO LOWER CASE
	JMP	SHORT K61		; NOT_CAPS_STATE
K59:					; NEAR-INTERRUPT-RETURN
	JMP	K26			; INTERRUPT_RETURN

;----- CONVERT ANY LOWER CASE TO UPPER CASE

K60:					; LOWER-TO-UPPER
	CMP	AL,'a'                  ; FIND OUT IF ALPHABETIC
	JB	K61			; NOT_CAPS_STATE
	CMP	AL,'z'
	JA	K61			; NOT_CAPS_STATE
	SUB	AL,'a'-'A'              ; CONVERT TO UPPER CASE
K61:					; NOT-CAPS-STATE
	MOV	BX,BUFFER_TAIL		; GET THE END POINTER TO THE BUFFER
	MOV	SI,BX			; SAVE THE VALUE
	CALL	K4			; ADVANCE THE TAIL
	CMP	BX,BUFFER_HEAD		; HAS THE BUFFER WRAPPED AROUND
	JE	K62			; BUFFER_FULL_BEEP
	MOV	[SI],AX 		; STORE THE VALUE
	MOV	BUFFER_TAIL,BX		; MOVE THE POINTER UP
	JMP	K26			; INTERRUPT_RETURN

;------ TRANSLATE SCAN FOR PSEUDO SCAN CODES

K63:					; TRANSLATE-SCAN
	SUB	AL,59			; CONVERT ORIGIN TO FUNCTION KEYS
K64:					; TRANSLATE-SCAN-ORGD
	XLAT	CS:K9			; CTL TABLE SCAN
	MOV	AH,AL			; PUT VALUE INTO AH
	MOV	AL,0			; ZERO ASCII CODE
	JMP	K57			; PUT IT INTO THE BUFFER

KB_INT	ENDP

;----- BUFFER IS FULL, SOUND THE BEEPER

K62:					; BUFFER-FULL-BEEP
	MOV	AL,EOI			; END OF INTERRUPT COMMAND
	OUT	20H,AL			; SEND COMMAND TO INT CONTROL PORT
	MOV	BX,80H			; NUMBER OF CYCLES FOR 1/12 SECOND TONE
	IN	AL,KB_CTL		; GET CONTROL INFORMATION
	PUSH	AX			; SAVE
K65:					; BEEP-CYCLE
	AND	AL,0FCH 		; TURN OFF TIMER GATE AND SPEAKER DATA
	OUT	KB_CTL,AL		; OUTPUT TO CONTROL
	MOV	CX,48H			; HALF CYCLE TIME FOR TONE
K66:
	LOOP	K66			; SPEAKER OFF
	OR	AL,2			; TURN ON SPEAKER BIT
	OUT	KB_CTL,AL		; OUTPUT TO CONTROL
	MOV	CX,48H			; SET UP COUNT
K67:
	LOOP	K67			; ANOTHER HALF CYCLE
	DEC	BX			; TOTAL TIME COUNT
	JNZ	K65			; DO ANOTHER CYCLE
	POP	AX			; RECOVER CONTROL
	OUT	KB_CTL,AL		; OUTPUT THE CONTROL
	JMP	K27

F1	DB	' 301',13,10            ; KEYBOARD ERROR
F3	DB	'601',13,10             ; DISKETTE ERROR

;-- INT 13 --------------------------------------------------------------
; DISKETTE I/O								:
;	THIS INTERFACE PROVIDES ACCESS TO THE 5 1/4" DISKETTE DRIVES    :
; INPUT 								:
;	(AH)=0	RESET DISKETTE SYSTEM					:
;		HARD RESET TO NEC, PREPARE COMMAND, RECAL REQUIRED	:
;		ON ALL DRIVES						:
;	(AH)=1	READ THE STATUS OF THE SYSTEM INTO (AL) 		:
;		DISKETTE_STATUS FROM LAST OPERATION IS USED		:
;									:
; REGISTERS FOR READ/WRITE/VERIFY/FORMAT				:
;	(DL) - DRIVE NUMBER (0-3 ALLOWED, VALUE CHECKED)		:
;	(DH) - HEAD NUMBER (0-1 ALLOWED, NOT VALUE CHECKED)		:
;	(CH) - TRACK NUMBER (0-39, NOT VALUE CHECKED)			:
;	(CL) - SECTOR NUMBER (1-8, NOT VALUE CHECKED,			:
;				 NOT USED FOR FORMAT)			:
;	(AL) - NUMBER OF SECTORS ( MAX = 8, NOT VALUE CHECKED, NOT USED :
;					FOR FORMAT)			:
;	(ES:BX) - ADDRESS OF BUFFER ( NOT REQUIRED FOR VERIFY)		:
;									:
;	(AH)=2	READ THE DESIRED SECTORS INTO MEMORY			:
;	(AH)=3	WRITE THE DESIRED SECTORS FROM MEMORY			:
;	(AH)=4	VERIFY THE DESIRED SECTORS				:
;	(AH)=5	FORMAT THE DESIRED TRACK				:
;		FOR THE FORMAT OPERATION, THE BUFFER POINTER (ES,BX)	:
;		MUST POINT TO THE COLLECTION OF DESIRED ADDRESS FIELDS	:
;		FOR THE TRACK.	EACH FIELD IS COMPOSED OF 4 BYTES,	:
;		(C,H,R,N), WHERE C = TRACK NUMBER, H=HEAD NUMBER,	:
;		R = SECTOR NUMBER, N= NUMBER OF BYTES PER SECTOR	:
;		(00=128, 01=256, 02=512, 03=1024).  THERE MUST BE ONE	:
;		ENTRY FOR EVERY SECTOR ON THE TRACK.  THIS INFORMATION	:
;		IS USED TO FIND THE REQUESTED SECTOR DURING READ/WRITE	:
;		ACCESS. 						:
;									:
; DATA VARIABLE -- DISK_POINTER 					:
;	DOUBLE WORD POINTER TO THE CURRENT SET OF DISKETTE PARAMETERS	:
; OUTPUT								:
;	AH = STATUS OF OPERATION					:
;		STATUS BITS ARE DEFINED IN THE EQUATES FOR		:
;		DISKETTE_STATUS VARIABLE IN THE DATA SEGMENT OF THIS	:
;		MODULE. 						:
;	CY = 0	SUCCESSFUL OPERATION (AH=0 ON RETURN)			:
;	CY = 1	FAILED OPERATION (AH HAS ERROR REASON)			:
;	FOR READ/WRITE/VERIFY						:
;		DS,BX,DX,CH,CL PRESERVED				:
;		AL = NUMBER OF SECTORS ACTUALLY READ			:
;		***** AL MAY NOT BE CORRECT IF TIME OUT ERROR OCCURS	:
;	NOTE:	IF AN ERROR IS REPORTED BY THE DISKETTE CODE, THE	:
;		APPROPRIATE ACTION IS TO RESET THE DISKETTE, THEN RETRY :
;		THE OPERATION. ON READ ACCESSES, NO MOTOR START DELAY	:
;		IS TAKEN, SO THAT THREE RETRIES ARE REQUIRED ON READS	:
;		TO ENSURE THAT THE PROBLEM IS NOT DUE TO MOTOR		:
;		START-UP.						:
;------------------------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA,ES:DATA
	ORG	0EC59H
DISKETTE_IO	PROC	FAR
	STI				; INTERRUPTS BACK ON
	PUSH	BX			; SAVE ADDRESS
	PUSH	CX
	PUSH	DS			; SAVE SEGMENT REGISTER VALUE
	PUSH	SI			; SAVE ALL REGISTERS DURING OPERATION
	PUSH	DI
	PUSH	BP
	PUSH	DX
	MOV	BP,SP			; SET UP POINTER TO HEAD PARM
	CALL	DDS
	CALL	J1			; CALL THE REST TO ENSURE DS RESTORED
	MOV	BX,4			; GET THE MOTOR WAIT PARAMETER
	CALL	GET_PARM
	MOV	MOTOR_COUNT,AH		; SET THE TIMER COST FOR THE MOTOR
	MOV	AH,DISKETTE_STATUS	; GET STATUS OF OPERATION
	CMP	AH,1			; SET THE CARRY FLAG TO INDICATE
	CMC				;  SUCCESS OR FAILURE
	POP	DX			; RESTORE ALL REGISTERS
	POP	BP
	POP	DI
	POP	SI
	POP	DS
	POP	CX
	POP	BX			; RECOVER ADDRESS
	RET	2			; THROW AWAY SAVED FLAGS
DISKETTE_IO	ENDP

J1	PROC	NEAR
	MOV	DH,AL			; SAVE # SECTORS IN DH
	AND	MOTOR_STATUS,07FH	; INDICATE A READ OPERATION
	OR	AH,AH			; AH=0
	JZ	DISK_RESET
	DEC	AH			; AH=1
	JZ	DISK_STATUS
	MOV	DISKETTE_STATUS,0	; RESET THE STATUS INDICATOR
	CMP	DL,4			; TEST FOR DRIVE IN 0-3 RANGE
	JAE	J3			; ERROR IF ABOVE
	DEC	AH			; AH=2
	JZ	DISK_READ
	DEC	AH			; AH=3
	JNZ	J2			; TEST_DISK_VERF
	JMP	DISK_WRITE
J2:					; TEST_DISK_VERF
	DEC	AH			; AH=4
	JZ	DISK_VERF
	DEC	AH			; AH=5
	JZ	DISK_FORMAT
J3:					; BAD_COMMAND
	MOV	DISKETTE_STATUS,BAD_CMD ; ERROR CODE, NO SECTORS TRANSFERRED
	RET				; UNDEFINED OPERATION
J1	ENDP

;----- RESET THE DISKETTE SYSTEM

DISK_RESET	PROC	NEAR
	MOV	DX,03F2H		; ADAPTER CONTROL PORT
	CLI				; NO INTERRUPTS
	MOV	AL,MOTOR_STATUS 	; WHICH MOTOR IS ON
	MOV	CL,4			; SHIFT COUNT
	SAL	AL,CL			; MOVE MOTOR VALUE TO HIGH NYBBLE
	TEST	AL, 20H 		; SELECT CORRESPONDING DRIVE
	JNZ	J5			; JUMP IF MOTOR ONE IS ON
	TEST	AL, 40H
	JNZ	J4			; JUMP IF MOTOR TWO IS ON
	TEST	AL, 80H
	JZ	J6			; JUMP IF MOTOR ZERO IS ON
	INC	AL
J4:
	INC	AL
J5:
	INC	AL
J6:
	OR	AL,8			; TURN ON INTERRUPT ENABLE
	OUT	DX,AL			; RESET THE ADAPTER
	MOV	SEEK_STATUS,0		; SET RECAL REQUIRED ON ALL DRIVES
	MOV	DISKETTE_STATUS,0	; SET OK STATUS FOR DISKETTE
	OR	AL,4			; TURN OFF RESET
	OUT	DX,AL			; TURN OFF THE RESET
	STI				; REENABLE THE INTERRUPTS
	CALL	CHK_STAT_2		; DO SENSE INTERRUPT STATUS
					;  FOLLOWING RESET
	MOV	AL,NEC_STATUS		; IGNORE ERROR RETURN AND DO OWN RESET
	CMP	AL,0C0H 		; TEST FOR DRIVE READY TRANSITION
	JZ	J7			; EVERYTHING OK
	OR	DISKETTE_STATUS,BAD_NEC ; SET ERROR CODE
	RET

;----- SEND SPECIFY COMMAND TO NEC

J7:					; DRIVE_READY
	MOV	AH,03H			; SPECIFY COMMAND
	CALL	NEC_OUTPUT		; OUTPUT THE COMMAND
	MOV	BX,1			; FIRST BYTE PARM IN BLOCK
	CALL	GET_PARM		;  TO THE NEC CONTROLLER
	MOV	BX,3			; SECOND BYTE PARM IN BLOCK
	CALL	GET_PARM		;  TO THE NEC CONTROLLER
J8:					; RESET_RET
	RET				; RETURN TO CALLER
DISK_RESET	ENDP

;----- DISKETTE STATUS ROUTINE

DISK_STATUS	PROC	NEAR
	MOV	AL,DISKETTE_STATUS
	RET
DISK_STATUS	ENDP

;----- DISKETTE READ

DISK_READ	PROC	NEAR
	MOV	AL,046H 		; READ COMMAND FOR DMA
J9:					; DISK_READ_CONT
	CALL	DMA_SETUP		; SET UP THE DMA
	MOV	AH,0E6H 		; SET UP RD COMMAND FOR NEC CONTROLLER
	JMP	SHORT RW_OPN		; GO DO THE OPERATION
DISK_READ	ENDP

;----- DISKETTE VERIFY

DISK_VERF	PROC	NEAR
	MOV	AL,042H 		; VERIFY COMMAND FOR DMA
	JMP	J9			; DO AS IF DISK READ
DISK_VERF	ENDP

;----- DISKETTE FORMAT

DISK_FORMAT	PROC	NEAR
	OR	MOTOR_STATUS,80H	; INDICATE WRITE OPERATION
	MOV	AL,04AH 		; WILL WRITE TO THE DISKETTE
	CALL	DMA_SETUP		; SET UP THE DMA
	MOV	AH,04DH 		; ESTABLISH THE FORMAT COMMAND
	JMP	SHORT RW_OPN		; DO THE OPERATION
J10:					; CONTINUATION OF RW_OPN FOR FMT
	MOV	BX,7			; GET THE
	CALL	GET_PARM		;  BYTES/SECTOR VALUE TO NEC
	MOV	BX,9			; GET THE
	CALL	GET_PARM		;  SECTORS/TRACK VALUE TO NEC
	MOV	BX,15			; GET THE
	CALL	GET_PARM		;  GAP LENGTH VALUE TO NEC
	MOV	BX,17			; GET THE FILLER BYTE
	JMP	J16			;  TO THE CONTROLLER
DISK_FORMAT	ENDP

;----- DISKETTE WRITE ROUTINE

DISK_WRITE	PROC	NEAR
	OR	MOTOR_STATUS,80H	; INDICATE WRITE OPERATION
	MOV	AL,04AH 		; DMA WRITE COMMAND
	CALL	DMA_SETUP
	MOV	AH,0C5H 		; NEC COMMAND TO WRITE TO DISKETTE
DISK_WRITE	ENDP

;----- ALLOW WRITE ROUTINE TO FALL INTO RW_OPN

;----------------------------------------------------------------
; RW_OPN							:
;	THIS ROUTINE PERFORMS THE READ/WRITE/VERIFY OPERATION	:
;----------------------------------------------------------------
RW_OPN	PROC	NEAR
	JNC	J11			; TEST FOR DMA ERROR
	MOV	DISKETTE_STATUS,DMA_BOUNDARY	; SET ERROR
	MOV	AL,0			; NO SECTORS TRANSFERRED
	RET				; RETURN TO MAIN ROUTINE
J11:					; DO_RW_OPN
	PUSH	AX			; SAVE THE COMMAND

;----- TURN ON THE MOTOR AND SELECT THE DRIVE

	PUSH	CX			; SAVE THE T/S PARMS
	MOV	CL,DL			; GET DRIVE NUMBER AS SHIFT COUNT
	MOV	AL,1			; MASK FOR DETERMINING MOTOR BIT
	SAL	AL,CL			; SHIFT THE MASK BIT
	CLI				; NO INTERRUPTS WHILE DETERMINING
					;  MOTOR STATUS
	MOV	MOTOR_COUNT,0FFH	; SET LARGE COUNT DURING OPERATION
	TEST	AL,MOTOR_STATUS 	; TEST THAT MOTOR FOR OPERATING
	JNZ	J14			; IF RUNNING, SKIP THE WAIT
	AND	MOTOR_STATUS,0F0H	; TURN OFF ALL MOTOR BITS
	OR	MOTOR_STATUS,AL 	; TURN ON THE CURRENT MOTOR
	STI				; INTERRUPTS BACK ON
	MOV	AL,10H			; MASK BIT
	SAL	AL,CL			; DEVELOP BIT MASK FOR MOTOR ENABLE
	OR	AL,DL			; GET DRIVE SELECT BITS IN
	OR	AL,0CH			; NO RESET, ENABLE DMA/INT
	PUSH	DX			; SAVE REG
	MOV	DX,03F2H		; CONTROL PORT ADDRESS
	OUT	DX,AL
	POP	DX			; RECOVER REGISTERS

;----- WAIT FOR MOTOR IF WRITE OPERATION

	TEST	MOTOR_STATUS,80H	; IS THIS A WRITE
	JZ	J14			; NO, CONTINUE WITHOUT WAIT
	MOV	BX,20			; GET THE MOTOR WAIT
	CALL	GET_PARM		;  PARAMETER
	OR	AH,AH			; TEST FOR NO WAIT
J12:					; TEST_WAIT_TIME
	JZ	J14			; EXIT WITH TIME EXPIRED
	SUB	CX,CX			; SET UP 1/8 SECOND LOOP TIME
J13:
	LOOP	J13			; WAIT FOR THE REQUIRED TIME
	DEC	AH			; DECREMENT TIME VALUE
	JMP	J12			; ARE WE DONE YET
J14:					; MOTOR_RUNNING
	STI				; INTERRUPTS BACK ON FOR BYPASS WAIT
	POP	CX

;----- DO THE SEEK OPERATION

	CALL	SEEK			; MOVE TO CORRECT TRACK
	POP	AX			; RECOVER COMMAND
	MOV	BH,AH			; SAVE COMMAND IN BH
	MOV	DH,0			; SET NO SECTORS READ IN CASE OF ERROR
	JC	J17			; IF ERROR, THEN EXIT AFTER MOTOR OFF
	MOV	SI,OFFSET J17		; DUMMY RETURN ON STACK FOR NEC_OUTPUT
	PUSH	SI			;  SO THAT IT WILL RETURN TO MOTOR OFF
					;  LOCATION

;----- SEND OUT THE PARAMETERS TO THE CONTROLLER

	CALL	NEC_OUTPUT		; OUTPUT THE OPERATION COMMAND
	MOV	AH,[BP+1]		; GET THE CURRENT HEAD NUMBER
	SAL	AH,1			; MOVE IT TO BIT 2
	SAL	AH,1
	AND	AH,4			; ISOLATE THAT BIT
	OR	AH,DL			; OR IN THE DRIVE NUMBER
	CALL	NEC_OUTPUT

;----- TEST FOR FORMAT COMMAND

	CMP	BH,04DH 		; IS THIS A FORMAT OPERATION
	JNE	J15			; NO, CONTINUE WITH R/W/V
	JMP	J10			; IF SO, HANDLE SPECIAL
J15:
	MOV	AH,CH			; CYLINDER NUMBER
	CALL	NEC_OUTPUT
	MOV	AH,[BP+1]		; HEAD NUMBER FROM STACK
	CALL	NEC_OUTPUT
	MOV	AH,CL			; SECTOR NUMBER
	CALL	NEC_OUTPUT
	MOV	BX,7			; BYTES/SECTOR PARM FROM BLOCK
	CALL	GET_PARM		;  TO THE NEC
	MOV	BX,9			; EOT PARM FROM BLOCK
	CALL	GET_PARM		;  TO THE NEC
	MOV	BX,11			; GAP LENGTH PARM FROM BLOCK
	CALL	GET_PARM		;  TO THE NEC
	MOV	BX,13			; DTL PARM FROM BLOCK
J16:					; RW_OPN FINISH
	CALL	GET_PARM		;  TO THE NEC
	POP	SI			; CAN NOW DISCARD THAT DUMMY
					;  RETURN ADDRESS

;----- LET THE OPERATION HAPPEN

	CALL	WAIT_INT		; WAIT FOR THE INTERRUPT
J17:					; MOTOR_OFF
	JC	J21			; LOOK FOR ERROR
	CALL	RESULTS 		; GET THE NEC STATUS
	JC	J20			; LOOK FOR ERROR

;----- CHECK THE RESULTS RETURNED BY THE CONTROLLER

	CLD				; SET THE CORRECT DIRECTION
	MOV	SI,OFFSET NEC_STATUS	; POINT TO STATUS FIELD
	LODS	NEC_STATUS		; GET ST0
	AND	AL,0C0H 		; TEST FOR NORMAL TERMINATION
	JZ	J22			; OPN_OK
	CMP	AL,040H 		; TEST FOR ABNORMAL TERMINATION
	JNZ	J18			; NOT ABNORMAL, BAD NEC

;----- ABNORMAL TERMINATION, FIND OUT WHY

	LODS	NEC_STATUS		; GET ST1
	SAL	AL,1			; TEST FOR EOT FOUND
	MOV	AH,RECORD_NOT_FND
	JC	J19			; RW_FAIL
	SAL	AL,1
	SAL	AL,1			; TEST FOR CRC ERROR
	MOV	AH,BAD_CRC
	JC	J19			; RW_FAIL
	SAL	AL,1			; TEST FOR DMA OVERRUN
	MOV	AH,BAD_DMA
	JC	J19			; RW_FAIL
	SAL	AL,1
	SAL	AL,1			; TEST FOR RECORD NOT FOUND
	MOV	AH,RECORD_NOT_FND
	JC	J19			; RW_FAIL
	SAL	AL,1
	MOV	AH,WRITE_PROTECT	; TEST FOR WRITE PROTECT
	JC	J19			; RW_FAIL
	SAL	AL,1			; TEST MISSING ADDRESS MARK
	MOV	AH,BAD_ADDR_MARK
	JC	J19			; RW_FAIL

;----- NEC MUST HAVE FAILED

J18:					; RW-NEC-FAIL
	MOV	AH,BAD_NEC
J19:					; RW-FAIL
	OR	DISKETTE_STATUS,AH
	CALL	NUM_TRANS		; HOW MANY WERE REALLY TRANSFERRED
J20:					; RW_ERR
	RET				; RETURN TO CALLER
J21:					; RW_ERR_RES
	CALL	RESULTS 		; FLUSH THE RESULTS BUFFER
	RET

;----- OPERATION WAS SUCCESSFUL

J22:					; OPN_OK
	CALL	NUM_TRANS		; HOW MANY GOT MOVED
	XOR	AH,AH			; NO ERRORS
	RET
RW_OPN	ENDP
;------------------------------------------------------------------------
; NEC_OUTPUT								:
;	THIS ROUTINE SENDS A BYTE TO THE NEC CONTROLLER AFTER TESTING	:
;	FOR CORRECT DIRECTION AND CONTROLLER READY THIS ROUTINE WILL	:
;	TIME OUT IF THE BYTE IS NOT ACCEPTED WITHIN A REASONABLE	:
;	AMOUNT OF TIME, SETTING THE DISKETTE STATUS ON COMPLETION	:
; INPUT 								:
;	(AH)	BYTE TO BE OUTPUT					:
; OUTPUT								:
;	CY = 0	SUCCESS 						:
;	CY = 1	FAILURE -- DISKETTE STATUS UPDATED			:
;		IF A FAILURE HAS OCCURRED, THE RETURN IS MADE ONE LEVEL :
;		HIGHER THAN THE CALLER OF NEC_OUTPUT			:
;		THIS REMOVES THE REQUIREMENT OF TESTING AFTER EVERY	:
;		CALL OF NEC_OUTPUT.					:
;	(AL) DESTROYED							:
;------------------------------------------------------------------------
NEC_OUTPUT	PROC	NEAR
	PUSH	DX			; SAVE REGISTERS
	PUSH	CX
	MOV	DX,03F4H		; STATUS PORT
	XOR	CX,CX			; COUNT FOR TIME OUT
J23:
	IN	AL,DX			; GET STATUS
	TEST	AL,040H 		; TEST DIRECTION BIT
	JZ	J25			; DIRECTION OK
	LOOP	J23
J24:					; TIME_ERROR
	OR	DISKETTE_STATUS,TIME_OUT
	POP	CX
	POP	DX			; SET ERROR CODE AND RESTORE REGS
	POP	AX			; DISCARD THE RETURN ADDRESS
	STC				; INDICATE ERROR TO CALLER
	RET
J25:
	XOR	CX,CX			; RESET THE COUNT
J26:
	IN	AL,DX			; GET THE STATUS
	TEST	AL,080H 		; IS IT READY
	JNZ	J27			; YES, GO OUTPUT
	LOOP	J26			; COUNT DOWN AND TRY AGAIN
	JMP	J24			; ERROR CONDITION
J27:					; OUTPUT
	MOV	AL,AH			; GET BYTE TO OUTPUT
	MOV	DL,0F5H 		; DATA PORT (3F5)
	OUT	DX,AL			; OUTPUT THE BYTE
	POP	CX			; RECOVER REGISTERS
	POP	DX
	RET				; CY = 0 FROM TEST INSTRUCTION
NEC_OUTPUT	ENDP
;------------------------------------------------------------------------
; GET_PARM								:
;	THIS ROUTINE FETCHES THE INDEXED POINTER FROM THE DISK_BASE	:
;	BLOCK POINTED AT BY THE DATA VARIABLE DISK_POINTER. A BYTE FROM :
;	THAT TABLE IS THEN MOVED INTO AH, THE INDEX OF THAT BYTE BEING	:
;	THE PARM IN BX							:
; ENTRY --								:
;   BX = INDEX OF BYTE TO BE FETCHED * 2				:
;	 IF THE LOW BIT OF BX IS ON, THE BYTE IS IMMEDIATELY		:
;	 OUTPUT TO THE NEC CONTROLLER					:
; EXIT --								:
;   AH = THAT BYTE FROM BLOCK						:
;------------------------------------------------------------------------
GET_PARM	PROC	NEAR
	PUSH	DS			; SAVE SEGMENT
	SUB	AX,AX			; ZERO TO AX
	MOV	DS,AX
	ASSUME	DS:ABS0
	LDS	SI,DISK_POINTER 	; POINT TO BLOCK
	SHR	BX,1			; DIVIDE BX BY 2, AND SET FLAG
					;  FOR EXIT
	MOV	AH,[SI+BX]		; GET THE WORD
	POP	DS			; RESTORE SEGMENT
	ASSUME	DS:DATA
	JC	NEC_OUTPUT		; IF FLAG SET, OUTPUT TO CONTROLLER
	RET				; RETURN TO CALLER
GET_PARM	ENDP
;------------------------------------------------------------------------
; SEEK									:
;	THIS ROUTINE WILL MOVE THE HEAD ON THE NAMED DRIVE TO THE	:
;	NAMED TRACK. IF THE DRIVE HAS NOT BEEN ACCESSED SINCE THE	:
;	DRIVE RESET COMMAND WAS ISSUED, THE DRIVE WILL BE RECALIBRATED. :
; INPUT 								:
;	(DL) = DRIVE TO SEEK ON 					:
;	(CH) = TRACK TO SEEK TO 					:
; OUTPUT								:
;	CY = 0 SUCCESS							:
;	CY = 1 FAILURE -- DISKETTE_STATUS SET ACCORDINGLY		:
;	(AX) DESTROYED							:
;------------------------------------------------------------------------
SEEK	PROC	NEAR
	MOV	AL,1			; ESTABLISH MASK FOR RECAL TEST
	PUSH	CX			; SAVE INPUT VALUES
	MOV	CL,DL			; GET DRIVE VALUE INTO CL
	ROL	AL,CL			; SHIFT IT BY THE DRIVE VALUE
	POP	CX			; RECOVER TRACK VALUE
	TEST	AL,SEEK_STATUS		; TEST FOR RECAL REQUIRED
	JNZ	J28			; NO_RECAL
	OR	SEEK_STATUS,AL		; TURN ON THE NO RECAL BIT IN FLAG
	MOV	AH,07H			; RECALIBRATE COMMAND
	CALL	NEC_OUTPUT
	MOV	AH,DL
	CALL	NEC_OUTPUT		; OUTPUT THE DRIVE NUMBER
	CALL	CHK_STAT_2		; GET THE INTERUPT AND SENSE INT STATUS
	JC	J32			; SEEK_ERROR

;----- DRIVE IS IN SYNCH WITH CONTROLLER, SEEK TO TRACK

J28:
	MOV	AH,0FH			; SEEK COMMAND TO NEC
	CALL	NEC_OUTPUT
	MOV	AH,DL			; DRIVE NUMBER
	CALL	NEC_OUTPUT
	MOV	AH,CH			; TRACK NUMBER
	CALL	NEC_OUTPUT
	CALL	CHK_STAT_2		; GET ENDING INTERRUPT AND
					;  SENSE STATUS

;----- WAIT FOR HEAD SETTLE

	PUSHF				; SAVE STATE FLAGS
	MOV	BX,18			; GET HEAD SETTLE PARAMETER
	CALL	GET_PARM
	PUSH	CX			; SAVE REGISTER
J29:					; HEAD_SETTLE
	MOV	CX,550			; 1 MS LOOP
	OR	AH,AH			; TEST FOR TIME EXPIRED
	JZ	J31
J30:
	LOOP	J30			; DELAY FOR 1 MS
	DEC	AH			; DECREMENT THE COUNT
	JMP	J29			; DO IT SOME MORE
J31:
	POP	CX			; RECOVER STATE
	POPF
J32:					; SEEK ERROR
	RET				; RETURN TO CALLER
SEEK	ENDP
;------------------------------------------------------------------------
; DMA_SETUP								:
;	THIS ROUTINE SETS UP THE DMA FOR READ/WRITE/VERIFY OPERATIONS.	:
; INPUT 								:
;	(AL) = MODE BYTE FOR THE DMA					:
;	(ES:BX) - ADDRESS TO READ/WRITE THE DATA			:
; OUTPUT								:
;	(AX) DESTROYED							:
;------------------------------------------------------------------------
DMA_SETUP	PROC	NEAR
	PUSH	CX			; SAVE THE REGISTER
	CLI				; NO MORE INTERRUPTS
	OUT	DMA+12,AL		; SET THE FIRST/LAST F/F
	PUSH	AX
	POP	AX
	OUT	DMA+11,AL		; OUTPUT THE MODE BYTE
	MOV	AX,ES			; GET THE ES VALUE
	MOV	CL,4			; SHIFT COUNT
	ROL	AX,CL			; ROTATE LEFT
	MOV	CH,AL			; GET HIGHEST NYBLE OF ES TO CH
	AND	AL,0F0H 		; ZERO THE LOW NYBBLE FROM SEGMENT
	ADD	AX,BX			; TEST FOR CARRY FROM ADDITION
	JNC	J33
	INC	CH			; CARRY MEANS HIGH 4 BITS MUST BE INC
J33:
	PUSH	AX			; SAVE START ADDRESS
	OUT	DMA+4,AL		; OUTPUT LOW ADDRESS
	MOV	AL,AH
	OUT	DMA+4,AL		; OUTPUT HIGH ADDRESS
	MOV	AL,CH			; GET HIGH 4 BITS
	AND	AL,0FH
	OUT	081H,AL 		; OUTPUT THE HIGH 4 BITS TO
					;  THE PAGE REGISTER

;----- DETERMINE COUNT

	MOV	AH,DH			; NUMBER OF SECTORS
	SUB	AL,AL			;  TIMES 256 INTO AX
	SHR	AX,1			; SECTORS * 128 INTO AX
	PUSH	AX
	MOV	BX,6			; GET THE BYTES/SECTOR PARM
	CALL	GET_PARM
	MOV	CL,AH			; USE AS SHIFT COUNT(0=128,1=256 ETC)
	POP	AX
	SHL	AX,CL			; MULTIPLY BY CORRECT AMOUNT
	DEC	AX			; -1 FOR DMA VALUE
	PUSH	AX			; SAVE COUNT VALUE
	OUT	DMA+5,AL		; LOW BYTE OF COUNT
	MOV	AL,AH
	OUT	DMA+5,AL		; HIGH BYTE OF COUNT
	STI				; INTERRUPTS BACK ON
	POP	CX			; RECOVER COUNT VALUE
	POP	AX			; RECOVER ADDRESS VALUE
	ADD	AX,CX			; ADD, TEST FOR 64K OVERFLOW
	POP	CX			; RECOVER REGISTER
	MOV	AL,2			; MODE FOR 8237
	OUT	DMA+10,AL		; INITIALIZE THE DISKETTE CHANNEL
	RET				; RETURN TO CALLER,
					;  CFL SET BY ABOVE IF ERROR
DMA_SETUP	ENDP
;------------------------------------------------------------------------
; CHK_STAT_2								:
;	THIS ROUTINE HANDLES THE INTERRUPT RECEIVED AFTER A		:
;	RECALIBRATE, SEEK, OR RESET TO THE ADAPTER.			:
;	THE INTERRUPT IS WAITED FOR, THE INTERRUPT STATUS SENSED,	:
;	AND THE RESULT RETURNED TO THE CALLER.				:
; INPUT 								:
;	NONE								:
; OUTPUT								:
;	CY = 0 SUCCESS							:
;	CY = 1 FAILURE -- ERROR IS IN DISKETTE_STATUS			:
;	(AX) DESTROYED							:
;------------------------------------------------------------------------
CHK_STAT_2	PROC	NEAR
	CALL	WAIT_INT		; WAIT FOR THE INTERRUPT
	JC	J34			; IF ERROR, RETURN IT
	MOV	AH,08H			; SENSE INTERRUPT STATUS COMMAND
	CALL	NEC_OUTPUT
	CALL	RESULTS 		; READ IN THE RESULTS
	JC	J34			; CHK2_RETURN
	MOV	AL,NEC_STATUS		; GET THE FIRST STATUS BYTE
	AND	AL,060H 		; ISOLATE THE BITS
	CMP	AL,060H 		; TEST FOR CORRECT VALUE
	JZ	J35			; IF ERROR, GO MARK IT
	CLC				; GOOD RETURN
J34:
	RET				; RETURN TO CALLER
J35:					; CHK2_ERROR
	OR	DISKETTE_STATUS,BAD_SEEK
	STC				; ERROR RETURN CODE
	RET
CHK_STAT_2	ENDP
;------------------------------------------------------------------------
; WAIT_INT								:
;	THIS ROUTINE WAITS FOR AN INTERRUPT TO OCCUR. A TIME OUT	:
;	ROUTINE TAKES PLACE DURING THE WAIT, SO THAT AN ERROR MAY BE	:
;	RETURNED IF THE DRIVE IS NOT READY.				:
; INPUT 								:
;	NONE								:
; OUTPUT								:
;	CY = 0 SUCCESS							:
;	CY = 1 FAILURE -- DISKETTE_STATUS IS SET ACCORDINGLY		:
;	(AX) DESTROYED							:
;------------------------------------------------------------------------
WAIT_INT	PROC	NEAR
	STI				; TURN ON INTERRUPTS, JUST IN CASE
	PUSH	BX
	PUSH	CX			; SAVE REGISTERS
	MOV	BL,2			; CLEAR THE COUNTERS
	XOR	CX,CX			; FOR 2 SECOND WAIT
J36:
	TEST	SEEK_STATUS,INT_FLAG	; TEST FOR INTERRUPT OCCURRING
	JNZ	J37
	LOOP	J36			; COUNT DOWN WHILE WAITING
	DEC	BL			; SECOND LEVEL COUNTER
	JNZ	J36
	OR	DISKETTE_STATUS,TIME_OUT	; NOTHING HAPPENED
	STC				; ERROR RETURN
J37:
	PUSHF				; SAVE CURRENT CARRY
	AND	SEEK_STATUS,NOT INT_FLAG	; TURN OFF INTERRUPT FLAG
	POPF				; RECOVER CARRY
	POP	CX
	POP	BX			; RECOVER REGISTERS
	RET				; GOOD RETURN CODE COMES
					;  FROM TEST INST
WAIT_INT	ENDP
;--------------------------------------------------------
; DISK_INT						:
;	THIS ROUTINE HANDLES THE DISKETTE INTERRUPT	:
; INPUT 						:
;	NONE						:
; OUTPUT						:
;	THE INTERRUPT FLAG IS SET IS SEEK_STATUS	:
;--------------------------------------------------------
	ORG	0EF57H
DISK_INT	PROC	FAR
	STI				; RE ENABLE INTERRUPTS
	PUSH	DS
	PUSH	AX
	CALL	DDS
	OR	SEEK_STATUS,INT_FLAG
	MOV	AL,20H			; END OF INTERRUPT MARKER
	OUT	20H,AL			; INTERRUPT CONTROL PORT
	POP	AX
	POP	DS			; RECOVER SYSTEM
	IRET				; RETURN FROM INTERRUPT
DISK_INT	ENDP
;------------------------------------------------------------------------
; RESULTS								:
;	THIS ROUTINE WILL READ ANYTHING THAT THE NEC CONTROLLER HAS	:
;	TO SAY FOLLOWING AN INTERRUPT.					:
; INPUT 								:
;	NONE								:
; OUTPUT								:
;	CY = 0	SUCCESSFUL TRANSFER					:
;	CY = 1	FAILURE -- TIME OUT IN WAITING FOR STATUS		:
;	NEC_STATUS AREA HAS STATUS BYTE LOADED INTO IT			:
;	(AH) DESTROYED							:
;------------------------------------------------------------------------
RESULTS PROC	NEAR
	CLD
	MOV	DI,OFFSET NEC_STATUS	; POINTER TO DATA AREA
	PUSH	CX			; SAVE COUNTER
	PUSH	DX
	PUSH	BX
	MOV	BL,7			; MAX STATUS BYTES

;----- WAIT FOR REQUEST FOR MASTER

J38:					; INPUT_LOOP
	XOR	CX,CX			; COUNTER
	MOV	DX,03F4H		; STATUS PORT
J39:					; WAIT FOR MASTER
	IN	AL,DX			; GET STATUS
	TEST	AL,080H 		; MASTER READY
	JNZ	J40A			; TEST_DIR
	LOOP	J39			; WAIT_MASTER
	OR	DISKETTE_STATUS,TIME_OUT
J40:					; RESULTS_ERROR
	STC				; SET ERROR RETURN
	POP	BX
	POP	DX
	POP	CX
	RET

;----- TEST THE DIRECTION BIT

J40A:
	IN	AL,DX			; GET STATUS REG AGAIN
	TEST	AL,040H 		; TEST DIRECTION BIT
	JNZ	J42			; OK TO READ STATUS
J41:					; NEC_FAIL
	OR	DISKETTE_STATUS,BAD_NEC
	JMP	J40			; RESULTS ERROR

;----- READ IN THE STATUS

J42:					; INPUT STATUS
	INC	DX			; POINT AT DATA PORT
	IN	AL,DX			; GET THE DATA
	MOV	[DI],AL 		; STORE THE BYTE
	INC	DI			; INCREMENT THE POINTER
	MOV	CX,10			; LOOP TO KILL TIME FOR NEC
J43:	LOOP	J43
	DEC	DX			; POINT AT STATUS PORT
	IN	AL,DX			; GET STATUS
	TEST	AL,010H 		; TEST FOR NEC STILL BUSY
	JZ	J44			; RESULTS DONE
	DEC	BL			; DECREMENT THE STATUS COUNTER
	JNZ	J38			; GO BACK FOR MORE
	JMP	J41			; CHIP HAS FAILED

;----- RESULT OPERATION IS DONE

J44:
	POP	BX
	POP	DX
	POP	CX			; RECOVER REGISTERS
	RET				; GOOD RETURN CODE FROM TEST INST
;----------------------------------------------------------------
; NUM_TRANS							:
;	THIS ROUTINE CALCULATES THE NUMBER OF SECTORS THAT	:
;	WERE ACTUALLY TRANSFERRED TO/FROM THE DISKETTE		:
; INPUT 							:
;	(CH) = CYLINDER OF OPERATION				:
;	(CL) = START SECTOR OF OPERATION			:
; OUTPUT							:
;	(AL) = NUMBER ACTUALLY TRANSFERRED			:
;	NO OTHER REGISTERS MODIFIED				:
;----------------------------------------------------------------
NUM_TRANS	PROC	NEAR
	MOV	AL,NEC_STATUS+3 	; GET CYLINDER ENDED UP ON
	CMP	AL,CH			; SAME AS THE STARTED
	MOV	AL,NEC_STATUS+5 	; GET ENDING SECTOR
	JZ	J45			; IF ON SAME CYL, THEN NO ADJUST
	MOV	BX,8
	CALL	GET_PARM		; GET EOT VALUE
	MOV	AL,AH			;  INTO AL
	INC	AL			; USE EOT+1 FOR CALCULATION
J45:
	SUB	AL,CL			; SUBTRACT START FROM END
	RET
NUM_TRANS	ENDP
RESULTS 	ENDP
;------------------------------------------------------------------------
; DISK_BASE								:
;	THIS IS THE SET OF PARAMETERS REQUIRED FOR DISKETTE OPERATION,	:
;	THEY ARE POINTED AT BY THE DATA VARIABLE DISK_POINTER. TO	:
;	MODIFY THE PARAMETERS, BUILD ANOTHER PARAMETER BLOCK AND POINT	:
;	DISK_POINTER TO IT						:
;------------------------------------------------------------------------
	ORG	0EFC7H
DISK_BASE	LABEL	BYTE
	DB	11001111B	; SRT=C, HD UNLOAD=0F - 1ST SPECIFY BYTE
	DB	2		; HD LOAD=1, MODE=DMA - 2ND SPECIFY BYTE
	DB	MOTOR_WAIT	; WAIT AFTER OPN TIL MOTOR OFF
	DB	2		; 512 BYTES/SECTOR
	DB	8		; EOT (LAST SECTOR ON TRACK)
	DB	02AH		; GAP LENGTH
	DB	0FFH		; DTL
	DB	050H		; GAP LENGTH FOR FORMAT
	DB	0F6H		; FILL BYTE FOR FORMAT
	DB	25		; HEAD SETTLE TIME (MILLISECONDS)
	DB	4		; MOTOR START TIME (1/8 SECONDS)

;--- INT 17 -------------------------------------------------------------
; PRINTER_IO								:
;	THIS ROUTINE PROVIDES COMMUNICATION WITH THE PRINTER		:
; INPUT 								:
;	(AH)=0	PRINT THE CHARACTER IN (AL)				:
;		ON RETURN, AH=1 IF CHARACTER COULD NOT BE PRINTED	:
;		(TIME OUT). OTHER BITS SET AS ON NORMAL STATUS CALL	:
;	(AH)=1	INITIALIZE THE PRINTER PORT				:
;		RETURNS WITH (AH) SET WITH PRINTER STATUS		:
;	(AH)=2	READ THE PRINTER STATUS INTO (AH)			:
;		7	6	5	4	3	2-1  0		:
;		|	|	|	|	|	|    |_TIME OUT :
;		|	|	|	|	|	|_ UNUSED	:
;		|	|	|	|	|_ 1 = I/O ERROR	:
;		|	|	|	|_ 1 = SELECTED 		:
;		|	|	|_ 1 = OUT OF PAPER			:
;		|	|_ 1 = ACKNOWLEDGE				:
;		|_ 1 = NOT BUSY 					:
;									:
;	(DX) = PRINTER TO BE USED (0,1,2) CORRESPONDING TO ACTUAL	:
;		VALUES IN PRINTER_BASE AREA				:
;									:
; DATA AREA PRINTER BASE CONTAINS THE BASE ADDRESS OF THE PRINTER	:
; CARD(S) AVAILABLE (LOCATED AT BEGINNING OF DATA SEGMENT,		:
; 408H ABSOLUTE, 3 WORDS)						:
;									:
; DATA AREA PRINT_TIM_OUT (BYTE) MAY BE CHANGED TO CAUSE DIFFERENT	:
; TIME OUT WAITS. DEFAULT=20						:
;									:
; REGISTERS	AH IS MODIFIED						:
;		ALL OTHERS UNCHANGED					:
;------------------------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA
	ORG	0EFD2H
PRINTER_IO	PROC	FAR
	STI				; INTERRUPTS BACK
	PUSH	DS			; SAVE SEGMENT
	PUSH	DX
	PUSH	SI
	PUSH	CX
	PUSH	BX
	CALL	DDS
	MOV	SI,DX			; GET PRINTER PARM
	MOV	BL,PRINT_TIM_OUT[SI]	; LOAD TIME-OUT PARM
	SHL	SI,1			; WORD OFFSET INTO TABLE
	MOV	DX,PRINTER_BASE[SI]	; GET BASE ADDRESS FOR PRINTER CARD
	OR	DX,DX			; TEST DX FOR ZERO,
					;  INDICATING NO PRINTER
	JZ	B1			; RETURN
	OR	AH,AH			; TEST FOR (AH)=0
	JZ	B2			; PRINT_AL
	DEC	AH			; TEST FOR (AH)=1
	JZ	B8			; INIT_PRT
	DEC	AH			; TEST FOR (AH)=2
	JZ	B5			; PRINTER STATUS
B1:					; RETURN
	POP	BX
	POP	CX
	POP	SI			; RECOVER REGISTERS
	POP	DX			; RECOVER REGISTERS
	POP	DS
	IRET

;------ PRINT THE CHARACTER IN (AL)

B2:
	PUSH	AX			; SAVE VALUE TO PRINT
	OUT	DX,AL			; OUTPUT CHAR TO PORT
	INC	DX			; POINT TO STATUS PORT
B3:
	SUB	CX,CX			; WAIT_BUSY
B3_1:
	IN	AL,DX			; GET STATUS
	MOV	AH,AL			; STATUS TO AH ALSO
	TEST	AL,80H			; IS THE PRINTER CURRENTLY BUSY
	JNZ	B4			; OUT_STROBE
	LOOP	B3_1			; TRY AGAIN
	DEC	BL			; DROP LOOP COUNT
	JNZ	B3			; GO TILL TIMEOUT ENDS
	OR	AH,1			; SET ERROR FLAG
	AND	AH,0F9H 		; TURN OFF THE OTHER BITS
	JMP	SHORT B7		; RETURN WITH ERROR FLAG SET
B4:					; OUT_STROBE
	MOV	AL,0DH			; SET THE STROBE HIGH
	INC	DX			; STROBE IS BIT 0 OF PORT C OF 8255
	OUT	DX,AL
	MOV	AL,0CH			; SET THE STROBE LOW
	OUT	DX,AL
	POP	AX			; RECOVER THE OUTPUT CHAR

;------ PRINTER STATUS

B5:
	PUSH	AX			; SAVE AL REG
B6:
	MOV	DX,PRINTER_BASE[SI]
	INC	DX
	IN	AL,DX			; GET PRINTER STATUS
	MOV	AH,AL
	AND	AH,0F8H 		; TURN OFF USED BITS
B7:					; STATUS_SET
	POP	DX			; RECOVER AL REG
	MOV	AL,DL			; GET CHARACTER INTO AL
	XOR	AH,48H			; FLIP A COUPLE OF BITS
	JMP	B1			; RETURN FROM ROUTINE

;------ INITIALIZE THE PRINTER PORT

B8:
	PUSH	AX			; SAVE AL
	INC	DX			; POINT TO OUTPUT PORT
	INC	DX
	MOV	AL,8			; SET INIT LINE LOW
	OUT	DX,AL
	MOV	AX,1000
B9:					; INIT_LOOP
	DEC	AX			; LOOP FOR RESET TO TAKE
	JNZ	B9			; INIT_LOOP
	MOV	AL,0CH			; NO INTERRUPTS, NON AUTO LF,
					;  INIT HIGH
	OUT	DX,AL
	JMP	B6			; PRT_STATUS_1
PRINTER_IO	ENDP


;--- INT 10 -------------------------------------------------------------
; VIDEO_IO								:
;	THESE ROUTINES PROVIDE THE CRT INTERFACE			:
;	THE FOLLOWING FUNCTIONS ARE PROVIDED:				:
;	(AH)=0	SET MODE (AL) CONTAINS MODE VALUE			:
;		(AL)=0 40X25 BW (POWER ON DEFAULT)			:
;		(AL)=1 40X25 COLOR					:
;		(AL)=2 80X25 BW 					:
;		(AL)=3 80X25 COLOR					:
;		GRAPHICS MODES						:
;		(AL)=4 320X200 COLOR					:
;		(AL)=5 320X200 BW					:
;		(AL)=6 640X200 BW					:
;		CRT MODE=7 80X25 B&W CARD (USED INTERNAL TO VIDEO ONLY) :
;		*** NOTE BW MODES OPERATE SAME AS COLOR MODES, BUT	:
;			 COLOR BURST IS NOT ENABLED			:
;	(AH)=1	SET CURSOR TYPE 					:
;		(CH) = BITS 4-0 = START LINE FOR CURSOR 		:
;		       ** HARDWARE WILL ALWAYS CAUSE BLINK		:
;		       ** SETTING BIT 5 OR 6 WILL CAUSE ERRATIC 	:
;			  BLINKING OR NO CURSOR AT ALL			:
;		(CL) = BITS 4-0 = END LINE FOR CURSOR			:
;	(AH)=2	SET CURSOR POSITION					:
;		(DH,DL) = ROW,COLUMN  (0,0) IS UPPER LEFT		:
;		(DH) = PAGE NUMBER (MUST BE 0 FOR GRAPHICS MODES)	:
;	(AH)=3	READ CURSOR POSITION					:
;		(BH) = PAGE NUMBER (MUST BE 0 FOR GRAPHICS MODES)	:
;		ON EXIT (DH,DL) = ROW,COLUMN OF CURRENT CURSOR		:
;			(CH,CL) = CURSOR MODE CURRENTLY SET		:
;	(AH)=4	READ LIGHT PEN POSITION 				:
;		ON EXIT:						:
;		(AH) = 0 -- LIGHT PEN SWITCH NOT DOWN/NOT TRIGGERED	:
;		(AH) = 1 -- VALID LIGHT PEN VALUE IN REGISTERS		:
;			(DH,DL) = ROW, COLUMN OF CHARACTER LP POSN	:
;			(CH) = RASTER LINE (0-199)			:
;			(BX) = PIXEL COLUMN (0-319,639) 		:
;	(AH)=5	SELECT ACTIVE DISPLAY PAGE (VALID ONLY FOR ALPHA MODES) :
;		(AL)=NEW PAGE VAL (0-7 FOR MODES 0&1, 0-3 FOR MODES 2&3):
;	(AH)=6	SCROLL ACTIVE PAGE UP					:
;		(AL) = NUMBER OF LINES, INPUT LINES BLANKED AT BOTTOM	:
;		       OF WINDOW					:
;			AL = 0 MEANS BLANK ENTIRE WINDOW		:
;		(CH,CL) = ROW,COLUMN OF UPPER LEFT CORNER OF SCROLL	:
;		(DH,DL) = ROW,COLUMN OF LOWER RIGHT CORNER OF SCROLL	:
;		(BH) = ATTRIBUTE TO BE USED ON BLANK LINE		:
;	(AH)=7	SCROLL ACTIVE PAGE DOWN 				:
;		(AL) = NUMBER OF LINES, INPUT LINES BLANKED AT TOP	:
;		       OF WINDOW					:
;			AL = 0 MEANS BLANK ENTIRE WINDOW		:
;		(CH,CL) = ROW,COLUMN OF UPPER LEFT CORNER OF SCROLL	:
;		(DH,DL) = ROW,COLUMN OF LOWER RIGHT CORNER OF SCROLL	:
;		(BH) = ATTRIBUTE TO BE USED ON BLANK LINE		:
;									:
;	CHARACTER HANDLING ROUTINES					:
;									:
;	(AH) = 8 READ ATTRIBUTE/CHARACTER AT CURRENT CURSOR POSITION	:
;		(BH) = DISPLAY PAGE (VALID FOR ALPHA MODES ONLY)	:
;		ON EXIT:						:
;		(AL) = CHAR READ					:
;		(AH) = ATTRIBUTE OF CHARACTER READ (ALPHA MODES ONLY)	:
;	(AH) = 9 WRITE ATTRIBUTE/CHARACTER AT CURRENT CURSOR POSITION	:
;		(BH) = DISPLAY PAGE (VALID FOR ALPHA MODES ONLY)	:
;		(CX) = COUNT OF CHARACTERS TO WRITE			:
;		(AL) = CHAR TO WRITE					:
;		(BL) = ATTRIBUTE OF CHARACTER (ALPHA)/COLOR OF CHAR	:
;		       (GRAPHICS)					:
;			SEE NOTE ON WRITE DOT FOR BIT 7 OF BL = 1.	:
;	(AH) = 10 WRITE CHARACTER ONLY AT CURRENT CURSOR POSITION	:
;		(BH) = DISPLAY PAGE (VALID FOR ALPHA MODES ONLY)	:
;		(CX) = COUNT OF CHARACTERS TO WRITE			:
;		(AL) = CHAR TO WRITE					:
;	FOR READ/WRITE CHARACTER INTERFACE WHILE IN GRAPHICS MODE, THE	:
;		CHARACTERS ARE FORMED FROM A CHARACTER GENERATOR IMAGE	:
;		MAINTAINED IN THE SYSTEM ROM.  ONLY THE 1ST 128 CHARS	:
;		ARE CONTAINED THERE.  TO READ/WRITE THE SECOND 128	:
;		CHARS. THE USER MUST INITIALIZE THE POINTER AT		:
;		INTERRUPT 1FH (LOCATION 0007CH) TO POINT TO THE 1K BYTE :
;		TABLE CONTAINING THE CODE POINTS FOR THE SECOND 	:
;		128 CHARS (128-255).					:
;	FOR WRITE CHARACTER INTERFACE IN GRAPHICS MODE, THE REPLICATION :
;		FACTOR CONTAINED IN (CX) ON ENTRY WILL PRODUCE VALID	:
;		RESULTS ONLY FOR CHARACTERS CONTAINED ON THE SAME ROW.	:
;		CONTINUATION TO SUCCEEDING LINES WILL NOT PRODUCE	:
;		CORRECTLY.						:
;									:
;	GRAPHICS INTERFACE						:
;	(AH) = 11 SET COLOR PALETTE					:
;		(BH) = PALETTE COLOR ID BEING SET (0-127)		:
;		(BL) = COLOR VALUE TO BE USED WITH THAT COLOR ID	:
;		   NOTE: FOR THE CURRENT COLOR CARD, THIS ENTRY POINT	:
;			 HAS MEANING ONLY FOR 320X200 GRAPHICS. 	:
;			COLOR ID = 0 SELECTS THE BACKGROUND COLOR (0-15):
;			COLOR ID = 1 SELECTS THE PALETTE TO BE USED:	:
;				0 = GREEN(1)/RED(2)/YELLOW(3)		:
;				1 = CYAN(1)/MAGENTA(2)/WHITE(3) 	:
;			IN 40X25 OR 80X25 ALPHA MODES, THE VALUE SET	:
;				FOR PALETTE COLOR 0 INDICATES THE	:
;				BORDER COLOR TO BE USED (VALUES 0-31,	:
;				WHERE 16-31 SELECT THE HIGH INTENSITY	:
;				BACKGROUND SET. 			:
;	(AH) = 12 WRITE DOT						:
;		(DX) = ROW NUMBER					:
;		(CX) = COLUMN NUMBER					:
;		(AL) = COLOR VALUE					:
;			IF BIT 7 OF AL = 1, THEN THE COLOR VALUE IS	:
;			EXCLUSIVE OR'D WITH THE CURRENT CONTENTS OF     :
;			THE DOT 					:
;	(AH) = 13 READ DOT						:
;		(DX) = ROW NUMBER					:
;		(CX) = COLUMN NUMBER					:
;		(AL) RETURNS THE DOT READ				:
;									:
; ASCII TELETYPE ROUTINE FOR OUTPUT					:
;									:
;	(AH) = 14 WRITE TELETYPE TO ACTIVE PAGE 			:
;		(AL) = CHAR TO WRITE					:
;		(BL) = FOREGROUND COLOR IN GRAPHICS MODE		:
;		NOTE -- SCREEN WIDTH IS CONTROLLED BY PREVIOUS MODE SET :
;									:
;	(AH) = 15 CURRENT VIDEO STATE					:
;		RETURNS THE CURRENT VIDEO STATE 			:
;		(AL) = MODE CURRENTLY SET (SEE AH=0 FOR EXPLANATION)	:
;		(AH) = NUMBER OF CHARACTER COLUMNS ON SCREEN		:
;		(BH) = CURRENT ACTIVE DISPLAY PAGE			:
;									:
;	CS,SS,DS,ES,BX,CX,DX PRESERVED DURING CALL			:
;	ALL OTHERS DESTROYED						:
;------------------------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA,ES:VIDEO_RAM
	ORG	0F045H
M1	LABEL	WORD			; TABLE OF ROUTINES WITHIN VIDEO I/O
	DW	OFFSET	SET_MODE
	DW	OFFSET	SET_CTYPE
	DW	OFFSET	SET_CPOS
	DW	OFFSET	READ_CURSOR
	DW	OFFSET	READ_LPEN
	DW	OFFSET	ACT_DISP_PAGE
	DW	OFFSET	SCROLL_UP
	DW	OFFSET	SCROLL_DOWN
	DW	OFFSET	READ_AC_CURRENT
	DW	OFFSET	WRITE_AC_CURRENT
	DW	OFFSET	WRITE_C_CURRENT
	DW	OFFSET	SET_COLOR
	DW	OFFSET	WRITE_DOT
	DW	OFFSET	READ_DOT
	DW	OFFSET	WRITE_TTY
	DW	OFFSET	VIDEO_STATE
M1L	EQU	$-M1

	ORG	0F065H
VIDEO_IO	PROC	NEAR
	STI				; INTERRUPTS BACK ON
	CLD				; SET DIRECTION FORWARD
	PUSH	ES
	PUSH	DS			; SAVE SEGMENT REGISTERS
	PUSH	DX
	PUSH	CX
	PUSH	BX
	PUSH	SI
	PUSH	DI
	PUSH	AX			; SAVE AX VALUE
	MOV	AL,AH			; GET INTO LOW BYTE
	XOR	AH,AH			; ZERO TO HIGH BYTE
	SAL	AX,1			; *2 FOR TABLE LOOKUP
	MOV	SI,AX			; PUT INTO SI FOR BRANCH
	CMP	AX,M1L			; TEST FOR WITHIN RANGE
	JB	M2			; BRANCH AROUND BRANCH
	POP	AX			; THROW AWAY THE PARAMETER
	JMP	VIDEO_RETURN		; DO NOTHING IF NOT IN RANGE
M2:
	CALL	DDS
	MOV	AX,0B800H		; SEGMENT FOR COLOR CARD
	MOV	DI,EQUIP_FLAG		; GET EQUIPMENT SETTING
	AND	DI,30H			; ISOLATE CRT SWITCHES
	CMP	DI,30H			; IS SETTING FOR BW CARD?
	JNE	M3
	MOV	AH,0B0H 		; SEGMENT FOR BW CARD
M3:
	MOV	ES,AX			; SET UP TO POINT AT VIDEO RAM AREAS
	POP	AX			; RECOVER VALUE
	MOV	AH,CRT_MODE		; GET CURRENT MODE INTO AH
	JMP	WORD PTR CS:[SI+OFFSET M1]
VIDEO_IO	ENDP
;--------------------------------------------------------
; SET_MODE						:
;	THIS ROUTINE INITIALIZES THE ATTACHMENT TO	:
;	THE SELECTED MODE.  THE SCREEN IS BLANKED.	:
; INPUT 						:
;	(AL) = MODE SELECTED (RANGE 0-9)		:
; OUTPUT						:
;	NONE						:
;--------------------------------------------------------

;----- TABLES FOR USE IN SETTING OF MODE

	ORG	0F0A4H
VIDEO_PARMS	LABEL	BYTE
;----- INIT_TABLE
	DB	38H,28H,2DH,0AH,1FH,6,19H	; SET UP FOR 40X25
	DB	1CH,2,7,6,7
	DB	0,0,0,0
M4	EQU	$-VIDEO_PARMS

	DB	71H,50H,5AH,0AH,1FH,6,19H	; SET UP FOR 80X25
	DB	1CH,2,7,6,7
	DB	0,0,0,0

	DB	38H,28H,2DH,0AH,7FH,6,64H	; SET UP FOR GRAPHICS
	DB	70H,2,1,6,7
	DB	0,0,0,0

	DB	61H,50H,52H,0FH,19H,6,19H	; SET UP FOR 80X25 B&W CARD
	DB	19H,2,0DH,0BH,0CH
	DB	0,0,0,0

M5	LABEL	WORD			; TABLE OF REGEN LENGTHS
	DW	2048			; 40X25
	DW	4096			; 80X25
	DW	16384			; GRAPHICS
	DW	16384

;----- COLUMNS

M6	LABEL	BYTE
	DB	40,40,80,80,40,40,80,80

;----- C_REG_TAB

M7	LABEL	BYTE			; TABLE OF MODE SETS
	DB	2CH,28H,2DH,29H,2AH,2EH,1EH,29H

SET_MODE	PROC	NEAR
	MOV	DX,03D4H		; ADDRESS OF COLOR CARD
	MOV	BL,0			; MODE SET FOR COLOR CARD
	CMP	DI,30H			; IS BW CARD INSTALLED
	JNE	M8			; OK WITH COLOR
	MOV	AL,7			; INDICATE BW CARD MODE
	MOV	DL,0B4H 		; ADDRESS OF BW CARD
	INC	BL			; MODE SET FOR BW CARD
M8:
	MOV	AH,AL			; SAVE MODE IN AH
	MOV	CRT_MODE,AL		; SAVE IN GLOBAL VARIABLE
	MOV	ADDR_6845,DX		; SAVE ADDRESS OF BASE
	PUSH	DS			; SAVE POINTER TO DATA SEGMENT
	PUSH	AX			; SAVE MODE
	PUSH	DX			; SAVE OUTPUT PORT VALUE
	ADD	DX,4			; POINT TO CONTROL REGISTER
	MOV	AL,BL			; GET MODE SET FOR CARD
	OUT	DX,AL			; RESET VIDEO
	POP	DX			; BACK TO BASE REGISTER
	SUB	AX,AX			; SET UP FOR ABS0 SEGMENT
	MOV	DS,AX			; ESTABLISH VECTOR TABLE ADDRESSING
	ASSUME	DS:ABS0
	LDS	BX,PARM_PTR		; GET POINTER TO VIDEO PARMS
	POP	AX			; RECOVER PARMS
	ASSUME	DS:CODE
	MOV	CX,M4			; LENGTH OF EACH ROW OF TABLE
	CMP	AH,2			; DETERMINE WHICH ONE TO USE
	JC	M9			; MODE IS 0 OR 1
	ADD	BX,CX			; MOVE TO NEXT ROW OF INIT_TABLE
	CMP	AH,4
	JC	M9			; MODE IS 2 OR 3
	ADD	BX,CX			; MOVE TO GRAPHICS ROW OF INIT_TABLE
	CMP	AH,7
	JC	M9			; MODE IS 4,5, OR 6
	ADD	BX,CX			; MOVE TO BW CARD ROW OF INIT_TABLE

;----- BX POINTS TO CORRECT ROW OF INITIALIZATION TABLE

M9:					; OUT_INIT
	PUSH	AX			; SAVE MODE IN AH
	XOR	AH,AH			; AH WILL SERVE AS REGISTER
					;  NUMBER DURING LOOP

;----- LOOP THROUGH TABLE, OUTPUTTING REG ADDRESS, THEN VALUE FROM TABLE

M10:					; INIT LOOP
	MOV	AL,AH			; GET 6845 REGISTER NUMBER
	OUT	DX,AL
	INC	DX			; POINT TO DATA PORT
	INC	AH			; NEXT REGISTER VALUE
	MOV	AL,[BX] 		; GET TABLE VALUE
	OUT	DX,AL			; OUT TO CHIP
	INC	BX			; NEXT IN TABLE
	DEC	DX			; BACK TO POINTER REGISTER
	LOOP	M10			; DO THE WHOLE TABLE
	POP	AX			; GET MODE BACK
	POP	DS			; RECOVER SEGMENT VALUE
	ASSUME	DS:DATA

;----- FILL REGEN AREA WITH BLANK

	XOR	DI,DI			; SET UP POINTER FOR REGEN
	MOV	CRT_START,DI		; START ADDRESS SAVED IN GLOBAL
	MOV	ACTIVE_PAGE,0		; SET PAGE VALUE
	MOV	CX,8192 		; NUMBER OF WORDS IN COLOR CARD
	CMP	AH,4			; TEST FOR GRAPHICS
	JC	M12			; NO_GRAPHICS_INIT
	CMP	AH,7			; TEST FOR BW CARD
	JE	M11			; BW_CARD_INIT
	XOR	AX,AX			; FILL FOR GRAPHICS MODE
	JMP	SHORT M13		; CLEAR_BUFFER
M11:					; BW_CARD_INIT
	MOV	CH,08H			; BUFFER SIZE ON BW CARD
M12:					; NO_GRAPHICS_INIT
	MOV	AX,' '+7*256            ; FILL CHAR FOR ALPHA
M13:					; CLEAR BUFFER
	REP	STOSW			; FILL THE REGEN BUFFER WITH BLANKS

;----- ENABLE VIDEO AND CORRECT PORT SETTING

	MOV	CURSOR_MODE,607H	; SET CURRENT CURSOR MODE
	MOV	AL,CRT_MODE		; GET THE MODE
	XOR	AH,AH			;  INTO AX REGISTER
	MOV	SI,AX			; TABLE POINTER, INDEXED BY MODE
	MOV	DX,ADDR_6845		; PREPARE TO OUTPUT TO
					;  VIDEO ENABLE PORT
	ADD	DX,4
	MOV	AL,CS:[SI+OFFSET M7]
	OUT	DX,AL			; SET VIDEO ENABLE PORT
	MOV	CRT_MODE_SET,AL 	; SAVE THAT VALUE

;----- DETERMINE NUMBER OF COLUMNS, BOTH FOR ENTIRE DISPLAY
;----- AND THE NUMBER TO BE USED FOR TTY INTERFACE

	MOV	AL,CS:[SI + OFFSET M6]
	XOR	AH,AH
	MOV	CRT_COLS,AX		; NUMBER OF COLUMNS IN THIS SCREEN

;----- SET CURSOR POSITIONS

	AND	SI,0EH			; WORD OFFSET INTO CLEAR LENGTH TABLE
	MOV	CX,CS:[SI+OFFSET M5]	; LENGTH TO CLEAR
	MOV	CRT_LEN,CX		; SAVE LENGTH OF CRT -- NOT USED FOR BW
	MOV	CX,8			; CLEAR ALL CURSOR POSITIONS
	MOV	DI,OFFSET CURSOR_POSN
	PUSH	DS			; ESTABLISH SEGMENT
	POP	ES			;   ADDRESSING
	XOR	AX,AX
	REP	STOSW			; FILL WITH ZEROES

;----- SET UP OVERSCAN REGISTER

	INC	DX			; SET OVERSCAN PORT TO A DEFAULT
	MOV	AL,30H			; VALUE OF 30H FOR ALL MODES
					;  EXCEPT 640X200
	CMP	CRT_MODE,6		; SEE IF THE MODE IS 640X200 BW
	JNZ	M14			; IF IT ISNT 640X200, THEN GOTO REGULAR
	MOV	AL,3FH			; IF IT IS 640X200, THEN PUT IN 3FH
M14:
	OUT	DX,AL			; OUTPUT THE CORRECT VALUE TO 3D9 PORT
	MOV	CRT_PALETTE,AL		; SAVE THE VALUE FOR FUTURE USE

;----- NORMAL RETURN FROM ALL VIDEO RETURNS

VIDEO_RETURN:
	POP	DI
	POP	SI
	POP	BX
M15:					; VIDEO_RETURN_C
	POP	CX
	POP	DX
	POP	DS
	POP	ES			; RECOVER SEGMENTS
	IRET				; ALL DONE
SET_MODE	ENDP
;----------------------------------------------------------------
; SET_CTYPE							:
;	THIS ROUTINE SETS THE CURSOR VALUE			:
; INPUT 							:
;	(CX) HAS CURSOR VALUE CH-START LINE, CL-STOP LINE	:
; OUTPUT							:
;	NONE							:
;----------------------------------------------------------------
SET_CTYPE	PROC	NEAR
	MOV	AH,10			; 6845 REGISTER FOR CURSOR SET
	MOV	CURSOR_MODE,CX		; SAVE IN DATA AREA
	CALL	M16			; OUTPUT CX REG
	JMP	VIDEO_RETURN

;----- THIS ROUTINE OUTPUTS THE CX REGISTER TO THE 6845 REGS NAMED IN AH

M16:
	MOV	DX,ADDR_6845		; ADDRESS REGISTER
	MOV	AL,AH			; GET VALUE
	OUT	DX,AL			; REGISTER SET
	INC	DX			; DATA REGISTER
	MOV	AL,CH			; DATA
	OUT	DX,AL
	DEC	DX
	MOV	AL,AH
	INC	AL			; POINT TO OTHER DATA REGISTER
	OUT	DX,AL			; SET FOR SECOND REGISTER
	INC	DX
	MOV	AL,CL			; SECOND DATA VALUE
	OUT	DX,AL
	RET				; ALL DONE
SET_CTYPE	ENDP
;------------------------------------------------
; SET_CPOS					:
;	THIS ROUTINE SETS THE CURRENT CURSOR	:
;	POSITION TO THE NEW X-Y VALUES PASSED	:
; INPUT 					:
;	DX - ROW, COLUMN OF NEW CURSOR		:
;	BH - DISPLAY PAGE OF CURSOR		:
; OUTPUT					:
;	CURSOR IS SET AT 6845 IF DISPLAY PAGE	:
;	IS CURRENT DISPLAY			:
;------------------------------------------------
SET_CPOS	PROC	NEAR
	MOV	CL,BH
	XOR	CH,CH			; ESTABLISH LOOP COUNT
	SAL	CX,1			; WORD OFFSET
	MOV	SI,CX			; USE INDEX REGISTER
	MOV	[SI+OFFSET CURSOR_POSN],DX	; SAVE THE POINTER
	CMP	ACTIVE_PAGE,BH
	JNZ	M17			; SET_CPOS_RETURN
	MOV	AX,DX			; GET ROW/COLUMN TO AX
	CALL	M18			; CURSOR_SET
M17:					; SET_CPOS_RETURN
	JMP	VIDEO_RETURN
SET_CPOS	ENDP

;----- SET CURSOR POSITION, AX HAS ROW/COLUMN FOR CURSOR

M18	PROC	NEAR
	CALL	POSITION		; DETERMINE LOCATION IN REGEN BUFFER
	MOV	CX,AX
	ADD	CX,CRT_START		; ADD IN THE START ADDR FOR THIS PAGE
	SAR	CX,1			; DIVIDE BY 2 FOR CHAR ONLY COUNT
	MOV	AH,14			; REGISTER NUMBER FOR CURSOR
	CALL	M16			; OUTPUT THE VALUE TO THE 6845
	RET
M18	ENDP
;----------------------------------------------------------------
; ACT_DISP_PAGE 						:
;	THIS ROUTINE SETS THE ACTIVE DISPLAY PAGE, ALLOWING THE :
;	FULL USE OF THE RAM SET ASIDE FOR THE VIDEO ATTACHMENT	:
; INPUT 							:
;	AL HAS THE NEW ACTIVE DISPLAY PAGE			:
; OUTPUT							:
;	THE 6845 IS RESET TO DISPLAY THAT PAGE			:
;----------------------------------------------------------------
ACT_DISP_PAGE	PROC	NEAR
	MOV	ACTIVE_PAGE,AL		; SAVE ACTIVE PAGE VALUE
	MOV	CX,CRT_LEN		; GET SAVED LENGTH OF REGEN BUFFER
	CBW				; CONVERT AL TO WORD
	PUSH	AX			; SAVE PAGE VALUE
	MUL	CX			; DISPLAY PAGE TIMES REGEN LENGTH
	MOV	CRT_START,AX		; SAVE START ADDRESS FOR
					;  LATER REQUIREMENTS
	MOV	CX,AX			; START ADDRESS TO CX
	SAR	CX,1			; DIVIDE BY 2 FOR 6845 HANDLING
	MOV	AH,12			; 6845 REGISTER FOR START ADDRESS
	CALL	M16
	POP	BX			; RECOVER PAGE VALUE
	SAL	BX,1			; *2 FOR WORD OFFSET
	MOV	AX,[BX+OFFSET CURSOR_POSN]	; GET CURSOR FOR THIS PAGE
	CALL	M18			; SET THE CURSOR POSITION
	JMP	SHORT VIDEO_RETURN
ACT_DISP_PAGE	ENDP
;----------------------------------------------------------------
; READ_CURSOR							:
;	THIS ROUTINE READS THE CURRENT CURSOR VALUE FROM THE	:
;	6845, FORMATS IT, AND SENDS IT BACK TO THE CALLER	:
; INPUT 							:
;	BH - PAGE OF CURSOR					:
; OUTPUT							:
;	DX - ROW, COLUMN OF THE CURRENT CURSOR POSITION 	:
;	CX - CURRENT CURSOR MODE				:
;----------------------------------------------------------------
READ_CURSOR	PROC	NEAR
	MOV	BL,BH
	XOR	BH,BH
	SAL	BX,1			; WORD OFFSET
	MOV	DX,[BX+OFFSET CURSOR_POSN]
	MOV	CX,CURSOR_MODE
	POP	DI
	POP	SI
	POP	BX
	POP	AX			; DISCARD SAVED CX AND DX
	POP	AX
	POP	DS
	POP	ES
	IRET
READ_CURSOR	ENDP
;------------------------------------------------------------------------
; SET_COLOR								:
;	THIS ROUTINE WILL ESTABLISH THE BACKGROUND COLOR, THE OVERSCAN	:
;	COLOR, AND THE FOREGROUND COLOR SET FOR MEDIUM RESOLUTION	:
;	GRAPHICS							:
; INPUT 								:
;	(BH) HAS COLOR ID						:
;		IF BH=0, THE BACKGROUND COLOR VALUE IS SET		:
;			FROM THE LOW BITS OF BL (0-31)			:
;		IF BH=1, THE PALETTE SELECTION IS MADE			:
;			BASED ON THE LOW BIT OF BL:			:
;				0=GREEN, RED, YELLOW FOR COLORS 1,2,3	:
;				1=BLUE, CYAN, MAGENTA FOR COLORS 1,2,3	:
;	(BL) HAS THE COLOR VALUE TO BE USED				:
; OUTPUT								:
;	THE COLOR SELECTION IS UPDATED					:
;------------------------------------------------------------------------
SET_COLOR	PROC	NEAR
	MOV	DX,ADDR_6845		; I/O PORT FOR PALETTE
	ADD	DX,5			; OVERSCAN PORT
	MOV	AL,CRT_PALETTE		; GET THE CURRENT PALETTE VALUE
	OR	BH,BH			; IS THIS COLOR 0?
	JNZ	M20			; OUTPUT COLOR 1

;----- HANDLE COLOR 0 BY SETTING THE BACKGROUND COLOR

	AND	AL,0E0H 		; TURN OFF LOW 5 BITS OF CURRENT
	AND	BL,01FH 		; TURN OFF HIGH 3 BITS OF INPUT VALUE
	OR	AL,BL			; PUT VALUE INTO REGISTER
M19:					; OUTPUT THE PALETTE
	OUT	DX,AL			; OUTPUT COLOR SELECTION TO 3D9 PORT
	MOV	CRT_PALETTE,AL		; SAVE THE COLOR VALUE
	JMP	VIDEO_RETURN

;----- HANDLE COLOR 1 BY SELECTING THE PALETTE TO BE USED

M20:
	AND	AL,0DFH 		; TURN OFF PALETTE SELECT BIT
	SHR	BL,1			; TEST THE LOW ORDER BIT OF BL
	JNC	M19			; ALREADY DONE
	OR	AL,20H			; TURN ON PALETTE SELECT BIT
	JMP	M19			; GO DO IT
SET_COLOR	ENDP
;------------------------------------------------
; VIDEO STATE					:
;  RETURNS THE CURRENT VIDEO STATE IN AX	:
;  AH = NUMBER OF COLUMNS ON THE SCREEN 	:
;  AL = CURRENT VIDEO MODE			:
;  BH = CURRENT ACTIVE PAGE			:
;------------------------------------------------
VIDEO_STATE	PROC	NEAR
	MOV	AH,BYTE PTR CRT_COLS	; GET NUMBER OF COLUMNS
	MOV	AL,CRT_MODE		; CURRENT MODE
	MOV	BH,ACTIVE_PAGE		; GET CURRENT ACTIVE PAGE
	POP	DI			; RECOVER REGISTERS
	POP	SI
	POP	CX			; DISCARD SAVED BX
	JMP	M15			; RETURN TO CALLER
VIDEO_STATE	ENDP
;--------------------------------------------------------
; POSITION						:
;	THIS SERVICE ROUTINE CALCULATES THE REGEN	:
;	BUFFER ADDRESS OF A CHARACTER IN THE ALPHA MODE :
; INPUT 						:
;	AX = ROW, COLUMN POSITION			:
; OUTPUT						:
;	AX = OFFSET OF CHAR POSITION IN REGEN BUFFER	:
;--------------------------------------------------------
POSITION	PROC	NEAR
	PUSH	BX			; SAVE REGISTER
	MOV	BX,AX
	MOV	AL,AH			; ROWS TO AL
	MUL	BYTE PTR CRT_COLS	; DETERMINE BYTES TO ROW
	XOR	BH,BH
	ADD	AX,BX			; ADD IN COLUMN VALUE
	SAL	AX,1			; * 2 FOR ATTRIBUTE BYTES
	POP	BX
	RET
POSITION	ENDP
;--------------------------------------------------------
; SCROLL_UP						:
;	THIS ROUTINE MOVES A BLOCK OF CHARACTERS UP	:
;	ON THE SCREEN					:
; INPUT 						:
;	(AH) = CURRENT CRT MODE 			:
;	(AL) = NUMBER OF ROWS TO SCROLL 		:
;	(CX) = ROW/COLUMN OF UPPER LEFT CORNER		:
;	(DX) = ROW/COLUMN OF LOWER RIGHT CORNER 	:
;	(BH) = ATTRIBUTE TO BE USED ON BLANKED LINE	:
;	(DS) = DATA SEGMENT				:
;	(ES) = REGEN BUFFER SEGMENT			:
; OUTPUT						:
;	NONE -- THE REGEN BUFFER IS MODIFIED		:
;--------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA,ES:DATA
SCROLL_UP	PROC	NEAR
	MOV	BL,AL			; SAVE LINE COUNT IN BL
	CMP	AH,4			; TEST FOR GRAPHICS MODE
	JC	N1			; HANDLE SEPARATELY
	CMP	AH,7			; TEST FOR BW CARD
	JE	N1
	JMP	GRAPHICS_UP
N1:					; UP_CONTINUE
	PUSH	BX			; SAVE FILL ATTRIBUTE IN BH
	MOV	AX,CX			; UPPER LEFT POSITION
	CALL	SCROLL_POSITION 	; DO SETUP FOR SCROLL
	JZ	N7			; BLANK_FIELD
	ADD	SI,AX			; FROM ADDRESS
	MOV	AH,DH			; # ROWS IN BLOCK
	SUB	AH,BL			; # ROWS TO BE MOVED
N2:					; ROW_LOOP
	CALL	N10			; MOVE ONE ROW
	ADD	SI,BP
	ADD	DI,BP			; POINT TO NEXT LINE IN BLOCK
	DEC	AH			; COUNT OF LINES TO MOVE
	JNZ	N2			; ROW_LOOP
N3:					; CLEAR_ENTRY
	POP	AX			; RECOVER ATTRIBUTE IN AH
	MOV	AL,' '                  ; FILL WITH BLANKS
N4:					; CLEAR_LOOP
	CALL	N11			; CLEAR THE ROW
	ADD	DI,BP			; POINT TO NEXT LINE
	DEC	BL			; COUNTER OF LINES TO SCROLL
	JNZ	N4			; CLEAR_LOOP
N5:					; SCROLL_END
	CALL	DDS
	CMP	CRT_MODE,7		; IS THIS THE BLACK AND WHITE CARD
	JE	N6			; IF SO, SKIP THE MODE RESET
	MOV	AL,CRT_MODE_SET 	; GET THE VALUE OF THE MODE SET
	MOV	DX,03D8H		; ALWAYS SET COLOR CARD PORT
	OUT	DX,AL
N6:					; VIDEO_RET_HERE
	JMP	VIDEO_RETURN
N7:					; BLANK_FIELD
	MOV	BL,DH			; GET ROW COUNT
	JMP	N3			; GO CLEAR THAT AREA
SCROLL_UP	ENDP

;----- HANDLE COMMON SCROLL SET UP HERE

SCROLL_POSITION PROC	NEAR
	CMP	CRT_MODE,2		; TEST FOR SPECIAL CASE HERE
	JB	N9			; HAVE TO HANDLE 80X25 SEPARATELY
	CMP	CRT_MODE,3
	JA	N9

;----- 80X25 COLOR CARD SCROLL

	PUSH	DX
	MOV	DX,3DAH 		; GUARANTEED TO BE COLOR CARD HERE
	PUSH	AX
N8:					; WAIT_DISP_ENABLE
	IN	AL,DX			; GET PORT
	TEST	AL,8			; WAIT FOR VERTICAL RETRACE
	JZ	N8			; WAIT_DISP_ENABLE
	MOV	AL,25H
	MOV	DL,0D8H 		; DX=3D8
	OUT	DX,AL			; TURN OFF VIDEO
	POP	AX			; DURING VERTICAL RETRACE
	POP	DX
N9:
	CALL	POSITION		; CONVERT TO REGEN POINTER
	ADD	AX,CRT_START		; OFFSET OF ACTIVE PAGE
	MOV	DI,AX			; TO ADDRESS FOR SCROLL
	MOV	SI,AX			; FROM ADDRESS FOR SCROLL
	SUB	DX,CX			; DX = #ROWS, #COLS IN BLOCK
	INC	DH
	INC	DL			; INCREMENT FOR 0 ORIGIN
	XOR	CH,CH			; SET HIGH BYTE OF COUNT TO ZERO
	MOV	BP,CRT_COLS		; GET NUMBER OF COLUMNS IN DISPLAY
	ADD	BP,BP			; TIMES 2 FOR ATTRIBUTE BYTE
	MOV	AL,BL			; GET LINE COUNT
	MUL	BYTE PTR CRT_COLS	; DETERMINE OFFSET TO FROM ADDRESS
	ADD	AX,AX			; *2 FOR ATTRIBUTE BYTE
	PUSH	ES			; ESTABLISH ADDRESSING TO REGEN BUFFER
	POP	DS			;  FOR BOTH POINTERS
	CMP	BL,0			; 0 SCROLL MEANS BLANK FIELD
	RET				; RETURN WITH FLAGS SET
SCROLL_POSITION ENDP

;----- MOVE_ROW

N10	PROC	NEAR
	MOV	CL,DL			; GET # OF COLS TO MOVE
	PUSH	SI
	PUSH	DI			; SAVE START ADDRESS
	REP	MOVSW			; MOVE THAT LINE ON SCREEN
	POP	DI
	POP	SI			; RECOVER ADDRESSES
	RET
N10	ENDP

;----- CLEAR_ROW

N11	PROC	NEAR
	MOV	CL,DL			; GET # COLUMNS TO CLEAR
	PUSH	DI
	REP	STOSW			; STORE THE FILL CHARACTER
	POP	DI
	RET
N11	ENDP
;--------------------------------------------------------
; SCROLL_DOWN						:
;	THIS ROUTINE MOVES THE CHARACTERS WITHIN A	:
;	DEFINED BLOCK DOWN ON THE SCREEN, FILLING THE	:
;	TOP LINES WITH A DEFINED CHARACTER		:
; INPUT 						:
;	(AH) = CURRENT CRT MODE 			:
;	(AL) = NUMBER OF LINES TO SCROLL		:
;	(CX) = UPPER LEFT CORNER OF REGION		:
;	(DX) = LOWER RIGHT CORNER OF REGION		:
;	(BH) = FILL CHARACTER				:
;	(DS) = DATA SEGMENT				:
;	(ES) = REGEN SEGMENT				:
; OUTPUT						:
;	NONE -- SCREEN IS SCROLLED			:
;--------------------------------------------------------
SCROLL_DOWN	PROC	NEAR
	STD				; DIRECTION FOR SCROLL
	MOV	BL,AL			; LINE COUNT TO BL
	CMP	AH,4			; TEST FOR GRAPHICS
	JC	N12
	CMP	AH,7			; TEST FOR BW CARD
	JE	N12
	JMP	GRAPHICS_DOWN
N12:					; CONTINUE DOWN
	PUSH	BX			; SAVE ATTRIBUTE IN BH
	MOV	AX,DX			; LOWER RIGHT CORNER
	CALL	SCROLL_POSITION 	; GET REGEN LOCATION
	JZ	N16
	SUB	SI,AX			; SI IS FROM ADDRESS
	MOV	AH,DH			; GET TOTAL # ROWS
	SUB	AH,BL			; COUNT TO MOVE IN SCROLL
N13:
	CALL	N10			; MOVE ONE ROW
	SUB	SI,BP
	SUB	DI,BP
	DEC	AH
	JNZ	N13
N14:
	POP	AX			; RECOVER ATTRIBUTE IN AH
	MOV	AL,' '
N15:
	CALL	N11			; CLEAR ONE ROW
	SUB	DI,BP			; GO TO NEXT ROW
	DEC	BL
	JNZ	N15
	JMP	N5			; SCROLL_END
N16:
	MOV	BL,DH
	JMP	N14
SCROLL_DOWN	ENDP
;--------------------------------------------------------
; READ_AC_CURRENT					:
;	THIS ROUTINE READS THE ATTRIBUTE AND CHARACTER	:
;	AT THE CURRENT CURSOR POSITION AND RETURNS THEM :
;	TO THE CALLER					:
; INPUT 						:
;	(AH) = CURRENT CRT MODE 			:
;	(BH) = DISPLAY PAGE ( ALPHA MODES ONLY )	:
;	(DS) = DATA SEGMENT				:
;	(ES) = REGEN SEGMENT				:
; OUTPUT						:
;	(AL) = CHAR READ				:
;	(AH) = ATTRIBUTE READ				:
;--------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA,ES:DATA
READ_AC_CURRENT PROC	NEAR
	CMP	AH,4			; IS THIS GRAPHICS
	JC	P1
	CMP	AH,7			; IS THIS BW CARD
	JE	P1
	JMP	GRAPHICS_READ
P1:					; READ_AC_CONTINUE
	CALL	FIND_POSITION
	MOV	SI,BX			; ESTABLISH ADDRESSING IN SI

;----- WAIT FOR HORIZONTAL RETRACE

	MOV	DX,ADDR_6845		; GET BASE ADDRESS
	ADD	DX,6			; POINT AT STATUS PORT
	PUSH	ES			;
	POP	DS			; GET SEGMENT FOR QUICK ACCESS
P2:					; WAIT FOR RETRACE LOW
	IN	AL,DX			; GET STATUS
	TEST	AL,1			; IS HORZ RETRACE LOW
	JNZ	P2			; WAIT UNTIL IT IS
	CLI				; NO MORE INTERRUPTS
P3:					; WAIT FOR RETRACE HIGH
	IN	AL,DX			; GET STATUS
	TEST	AL,1			; IS IT HIGH
	JZ	P3			; WAIT UNTIL IT IS
	LODSW				; GET THE CHAR/ATTR
	JMP	VIDEO_RETURN
READ_AC_CURRENT ENDP

FIND_POSITION	PROC	NEAR
	MOV	CL,BH			; DISPLAY PAGE TO CX
	XOR	CH,CH
	MOV	SI,CX			; MOVE TO SI FOR INDEX
	SAL	SI,1			; * 2 FOR WORD OFFSET
	MOV	AX,[SI+OFFSET CURSOR_POSN]	; GET ROW/COLUMN OF THAT PAGE
	XOR	BX,BX			; SET START ADDRESS TO ZERO
	JCXZ	P5			; NO_PAGE
P4:					; PAGE_LOOP
	ADD	BX,CRT_LEN		; LENGTH OF BUFFER
	LOOP	P4
P5:					; NO_PAGE
	CALL	POSITION		; DETERMINE LOCATION IN REGEN
	ADD	BX,AX			; ADD TO START OF REGEN
	RET
FIND_POSITION	ENDP
;------------------------------------------------
; WRITE_AC_CURRENT				:
;	THIS ROUTINE WRITES THE ATTRIBUTE	:
;	AND CHARACTER AT THE CURRENT CURSOR	:
;	POSITION				:
; INPUT 					:
;	(AH) = CURRENT CRT MODE 		:
;	(BH) = DISPLAY PAGE			:
;	(CX) = COUNT OF CHARACTERS TO WRITE	:
;	(AL) = CHAR TO WRITE			:
;	(BL) = ATTRIBUTE OF CHAR TO WRITE	:
;	(DS) = DATA SEGMENT			:
;	(ES) = REGEN SEGMENT			:
; OUTPUT					:
;	NONE					:
;------------------------------------------------
WRITE_AC_CURRENT	PROC	NEAR
	CMP	AH,4			; IS THIS GRAPHICS
	JC	P6
	CMP	AH,7			; IS THIS BW CARD
	JE	P6
	JMP	GRAPHICS_WRITE
P6:					; WRITE_AC_CONTINUE
	MOV	AH,BL			; GET ATTRIBUTE TO AH
	PUSH	AX			; SAVE ON STACK
	PUSH	CX			; SAVE WRITE COUNT
	CALL	FIND_POSITION
	MOV	DI,BX			; ADDRESS TO DI REGISTER
	POP	CX			; WRITE COUNT
	POP	BX			; CHARACTER IN BX REG
P7:					; WRITE_LOOP

;----- WAIT FOR HORIZONTAL RETRACE

	MOV	DX,ADDR_6845		; GET BASE ADDRESS
	ADD	DX,6			; POINT AT STATUS PORT
P8:
	IN	AL,DX			; GET STATUS
	TEST	AL,1			; IS IT LOW
	JNZ	P8			; WAIT UNTIL IT IS
	CLI				; NO MORE INTERRUPTS
P9:
	IN	AL,DX			; GET STATUS
	TEST	AL,1			; IS IT HIGH
	JZ	P9			; WAIT UNTIL IT IS
	MOV	AX,BX			; RECOVER THE CHAR/ATTR
	STOSW				; PUT THE CHAR/ATTR
	STI				; INTERRUPTS BACK ON
	LOOP	P7			;  AS MANY TIMES AS REQUESTED
	JMP	VIDEO_RETURN
WRITE_AC_CURRENT	ENDP
;------------------------------------------------
; WRITE_C_CURRENT				:
;	THIS ROUTINE WRITES THE CHARACTER AT	:
;	THE CURRENT CURSOR POSITION, ATTRIBUTE	:
;	UNCHANGED				:
; INPUT 					:
;	(AH) = CURRENT CRT MODE 		:
;	(BH) = DISPLAY PAGE			:
;	(CX) = COUNT OF CHARACTERS TO WRITE	:
;	(AL) = CHAR TO WRITE			:
;	(DS) = DATA SEGMENT			:
;	(ES) = REGEN SEGMENT			:
;OUTPUT 					:
;	NONE					:
;------------------------------------------------
WRITE_C_CURRENT PROC	NEAR
	CMP	AH,4			; IS THIS GRAPHICS
	JC	P10
	CMP	AH,7			; IS THIS BW CARD
	JE	P10
	JMP	GRAPHICS_WRITE
P10:
	PUSH	AX			; SAVE ON STACK
	PUSH	CX			; SAVE WRITE COUNT
	CALL	FIND_POSITION
	MOV	DI,BX			; ADDRESS TO DI
	POP	CX			; WRITE COUNT
	POP	BX			; BL HAS CHAR TO WRITE
P11:					; WRITE_LOOP

;----- WAIT FOR HORIZONTAL RETRACE

	MOV	DX,ADDR_6845		; GET BASE ADDRESS
	ADD	DX,6			; POINT AT STATUS PORT
P12:
	IN	AL,DX			; GET STATUS
	TEST	AL,1			; IS IT LOW
	JNZ	P12			; WAIT UNTIL IT IS
	CLI				; NO MORE INTERRUPTS
P13:
	IN	AL,DX			; GET STATUS
	TEST	AL,1			; IS IT HIGH
	JZ	P13			; WAIT UNTIL IT IS
	MOV	AL,BL			; RECOVER CHAR
	STOSB				; PUT THE CHAR/ATTR
	STI				; INTERRUPTS BACK ON
	INC	DI			; BUMP POINTER PAST ATTRIBUTE
	LOOP	P11			;  AS MANY TIMES AS REQUESTED
	JMP	VIDEO_RETURN
WRITE_C_CURRENT ENDP
;----------------------------------------------------------------
; READ DOT  -- WRITE DOT					:
;	THESE ROUTINES WILL WRITE A DOT, OR READ THE DOT AT	:
;	THE INDICATED LOCATION					:
; ENTRY --							:
;   DX = ROW (0-199)	(THE ACTUAL VALUE DEPENDS ON THE MODE)	:
;   CX = COLUMN ( 0-639) ( THE VALUES ARE NOT RANGE CHECKED )	:
;   AL = DOT VALUE TO WRITE (1,2 OR 4 BITS DEPENDING ON MODE,	:
;	REQ'D FOR WRITE DOT ONLY, RIGHT JUSTIFIED)              :
;	BIT 7 OF AL=1 INDICATES XOR THE VALUE INTO THE LOCATION :
;   DS = DATA SEGMENT						:
;   ES = REGEN SEGMENT						:
;								:
; EXIT								:
;	AL = DOT VALUE READ, RIGHT JUSTIFIED, READ ONLY 	:
;----------------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA,ES:DATA
READ_DOT	PROC	NEAR
	CALL	R3			; DETERMINE BYTE POSITION OF DOT
	MOV	AL,ES:[SI]		; GET THE BYTE
	AND	AL,AH			; MASK OFF THE OTHER BITS IN THE BYTE
	SHL	AL,CL			; LEFT JUSTIFY THE VALUE
	MOV	CL,DH			; GET NUMBER OF BITS IN RESULT
	ROL	AL,CL			; RIGHT JUSTIFY THE RESULT
	JMP	VIDEO_RETURN		; RETURN FROM VIDEO IO
READ_DOT	ENDP

WRITE_DOT	PROC	NEAR
	PUSH	AX			; SAVE DOT VALUE
	PUSH	AX			;  TWICE
	CALL	R3			; DETERMINE BYTE POSITION OF THE DOT
	SHR	AL,CL			; SHIFT TO SET UP THE BITS FOR OUTPUT
	AND	AL,AH			; STRIP OFF THE OTHER BITS
	MOV	CL,ES:[SI]		; GET THE CURRENT BYTE
	POP	BX			; RECOVER XOR FLAG
	TEST	BL,80H			; IS IT ON
	JNZ	R2			; YES, XOR THE DOT
	NOT	AH			; SET THE MASK TO REMOVE THE
	AND	CL,AH			;  INDICATED BITS
	OR	AL,CL			; OR IN THE NEW VALUE OF THOSE BITS
R1:					; FINISH_DOT
	MOV	ES:[SI],AL		; RESTORE THE BYTE IN MEMORY
	POP	AX
	JMP	VIDEO_RETURN		; RETURN FROM VIDEO IO
R2:					; XOR_DOT
	XOR	AL,CL			; EXCLUSIVE OR THE DOTS
	JMP	R1			; FINISH UP THE WRITING
WRITE_DOT	ENDP
;--------------------------------------------------------
; THIS SUBROUTINE DETERMINES THE REGEN BYTE LOCATION	:
; OF THE INDICATED ROW COLUMN VALUE IN GRAPHICS MODE.	:
; ENTRY --						:
;  DX = ROW VALUE (0-199)				:
;  CX = COLUMN VALUE (0-639)				:
; EXIT --						:
;  SI = OFFSET INTO REGEN BUFFER FOR BYTE OF INTEREST	:
;  AH = MASK TO STRIP OFF THE BITS OF INTEREST		:
;  CL = BITS TO SHIFT TO RIGHT JUSTIFY THE MASK IN AH	:
;  DH = # BITS IN RESULT				:
;--------------------------------------------------------
R3	PROC	NEAR
	PUSH	BX			; SAVE BX DURING OPERATION
	PUSH	AX			; WILL SAVE AL DURING OPERATION

;----- DETERMINE 1ST BYTE IN IDICATED ROW BY MULTIPLYING ROW VALUE BY 40
;----- ( LOW BIT OF ROW DETERMINES EVEN/ODD, 80 BYTES/ROW

	MOV	AL,40
	PUSH	DX			; SAVE ROW VALUE
	AND	DL,0FEH 		; STRIP OFF ODD/EVEN BIT
	MUL	DL			; AX HAS ADDRESS OF 1ST BYTE
					;  OF INDICATED ROW
	POP	DX			; RECOVER IT
	TEST	DL,1			; TEST FOR EVEN/ODD
	JZ	R4			; JUMP IF EVEN ROW
	ADD	AX,2000H		; OFFSET TO LOCATION OF ODD ROWS
R4:					; EVEN_ROW
	MOV	SI,AX			; MOVE POINTER TO SI
	POP	AX			; RECOVER AL VALUE
	MOV	DX,CX			; COLUMN VALUE TO DX

;----- DETERMINE GRAPHICS MODE CURRENTLY IN EFFECT

;----------------------------------------------------------------
; SET UP THE REGISTERS ACCORDING TO THE MODE			:
;  CH = MASK FOR LOW OF COLUMN ADDRESS (7/3 FOR HIGH/MED RES)	:
;  CL = # OF ADDRESS BITS IN COLUMN VALUE (3/2 FOR H/M) 	:
;  BL = MASK TO SELECT BITS FROM POINTED BYTE (80H/C0H FOR H/M) :
;  BH = NUMBER OF VALID BITS IN POINTED BYTE (1/2 FOR H/M)	:
;----------------------------------------------------------------

	MOV	BX,2C0H
	MOV	CX,302H 		; SET PARMS FOR MED RES
	CMP	CRT_MODE,6
	JC	R5			; HANDLE IF MED RES
	MOV	BX,180H
	MOV	CX,703H 		; SET PARMS FOR HIGH RES

;----- DETERMINE BIT OFFSET IN BYTE FROM COLUMN MASK

R5:
	AND	CH,DL			; ADDRESS OF PEL WITHIN BYTE TO CH

;----- DETERMINE BYTE OFFSET FOR THIS LOCATION IN COLUMN

	SHR	DX,CL			; SHIFT BY CORRECT AMOUNT
	ADD	SI,DX			; INCREMENT THE POINTER
	MOV	DH,BH			; GET THE # OF BITS IN RESULT TO DH

;----- MULTIPLY BH (VALID BITS IN BYTE) BY CH (BIT OFFSET)

	SUB	CL,CL			; ZERO INTO STORAGE LOCATION
R6:
	ROR	AL,1			; LEFT JUSTIFY THE VALUE
					;  IN AL (FOR WRITE)
	ADD	CL,CH			; ADD IN THE BIT OFFSET VALUE
	DEC	BH			; LOOP CONTROL
	JNZ	R6			; ON EXIT, CL HAS SHIFT COUNT
					;  TO RESTORE BITS
	MOV	AH,BL			; GET MASK TO AH
	SHR	AH,CL			; MOVE THE MASK TO CORRECT LOCATION
	POP	BX			; RECOVER REG
	RET				; RETURN WITH EVERYTHING SET UP
R3	ENDP
;----------------------------------------------------------------
; SCROLL UP							:
;	THIS ROUTINE SCROLLS UP THE INFORMATION ON THE CRT	:
; ENTRY 							:
;	CH,CL = UPPER LEFT CORNER OF REGION TO SCROLL		:
;	DH,DL = LOWER RIGHT CORNER OF REGION TO SCROLL		:
;	 BOTH OF THE ABOVE ARE IN CHARACTER POSITIONS		:
;	BH = FILL VALUE FOR BLANKED LINES			:
;	AL = # LINES TO SCROLL (AL=0 MEANS BLANK THE ENTIRE	:
;	     FIELD)						:
;	DS = DATA SEGMENT					:
;	ES = REGEN SEGMENT					:
; EXIT								:
;	NOTHING, THE SCREEN IS SCROLLED 			:
;----------------------------------------------------------------
GRAPHICS_UP	PROC	NEAR
	MOV	BL,AL			; SAVE LINE COUNT IN BL
	MOV	AX,CX			; GET UPPER LEFT POSITION INTO AX REG

;----- USE CHARACTER SUBROUTINE FOR POSITIONING
;----- ADDRESS RETURNED IS MULTIPLIED BY 2 FROM CORRECT VALUE

	CALL	GRAPH_POSN
	MOV	DI,AX			; SAVE RESULT AS DESTINATION ADDRESS

;----- DETERMINE SIZE OF WINDOW

	SUB	DX,CX
	ADD	DX,101H 		; ADJUST VALUES
	SAL	DH,1			; MULTIPLY # ROWS BY 4
					;  SINCE 8 VERT DOTS/CHAR
	SAL	DH,1			;  AND EVEN/ODD ROWS

;----- DETERMINE CRT MODE

	CMP	CRT_MODE,6		; TEST FOR MEDIUM RES
	JNC	R7			; FIND_SOURCE

;----- MEDIUM RES UP

	SAL	DL,1			; # COLUMNS * 2, SINCE 2 BYTES/CHAR
	SAL	DI,1			; OFFSET *2 SINCE 2 BYTES/CHAR

;----- DETERMINE THE SOURCE ADDRESS IN THE BUFFER

R7:					; FIND_SOURCE
	PUSH	ES			; GET SEGMENTS BOTH POINTING TO REGEN
	POP	DS
	SUB	CH,CH			; ZERO TO HIGH OF COUNT REG
	SAL	BL,1			; MULTIPLY NUMBER OF LINES BY 4
	SAL	BL,1
	JZ	R11			; IF ZERO, THEN BLANK ENTIRE FIELD
	MOV	AL,BL			; GET NUMBER OF LINES IN AL
	MOV	AH,80			; 80 BYTES
	MUL	AH			; DETERMINE OFFSET TO SOURCE
	MOV	SI,DI			; SET UP SOURCE
	ADD	SI,AX			;  ADD IN OFFSET TO IT
	MOV	AH,DH			; NUMBER OF ROWS IN FIELD
	SUB	AH,BL			; DETERMINE NUMBER TO MOVE

;----- LOOP THROUGH, MOVING ONE ROW AT A TIME, BOTH EVEN AND ODD FIELDS

R8:					; ROW_LOOP
	CALL	R17			; MOVE ONE ROW
	SUB	SI,2000H-80		; MOVE TO NEXT ROW
	SUB	DI,2000H-80
	DEC	AH			; NUMBER OF ROWS TO MOVE
	JNZ	R8			; CONTINUE TILL ALL MOVED

;----- FILL IN THE VACATED LINE(S)

R9:					; CLEAR_ENTRY
	MOV	AL,BH			; ATTRIBUTE TO FILL WITH
R10:
	CALL	R18			; CLEAR THAT ROW
	SUB	DI,2000H-80		; POINT TO NEXT LINE
	DEC	BL			; NUMBER OF LINES TO FILL
	JNZ	R10			; CLEAR_LOOP
	JMP	VIDEO_RETURN		; EVERYTHING DONE
R11:					; BLANK_FIELD
	MOV	BL,DH			; SET BLANK COUNT TO
					;  EVERYTHING IN FIELD
	JMP	R9			; CLEAR THE FIELD
GRAPHICS_UP	ENDP
;----------------------------------------------------------------
; SCROLL DOWN							:
;	THIS ROUTINE SCROLLS DOWN THE INFORMATION ON THE CRT	:
; ENTRY 							:
;	CH,CL = UPPER LEFT CORNER OF REGION TO SCROLL		:
;	DH,DL = LOWER RIGHT CORNER OF REGION TO SCROLL		:
;	 BOTH OF THE ABOVE ARE IN CHARACTER POSITIONS		:
;	BH = FILL VALUE FOR BLANKED LINES			:
;	AL = # LINES TO SCROLL (AL=0 MEANS BLANK THE ENTIRE	:
;	     FIELD)						:
;	DS = DATA SEGMENT					:
;	ES = REGEN SEGMENT					:
; EXIT								:
;	NOTHING, THE SCREEN IS SCROLLED 			:
;----------------------------------------------------------------
GRAPHICS_DOWN	PROC	NEAR
	STD				; SET DIRECTION
	MOV	BL,AL			; SAVE LINE COUNT IN BL
	MOV	AX,DX			; GET LOWER RIGHT POSITION INTO AX REG

;----- USE CHARACTER SUBROUTINE FOR POSITIONING
;----- ADDRESS RETURNED IS MULTIPLIED BY 2 FROM CORRECT VALUE

	CALL	GRAPH_POSN
	MOV	DI,AX			; SAVE RESULT AS DESTINATION ADDRESS

;----- DETERMINE SIZE OF WINDOW

	SUB	DX,CX
	ADD	DX,101H 		; ADJUST VALUES
	SAL	DH,1			; MULTIPLY # ROWS BY 4
					;  SINCE 8 VERT DOTS/CHAR
	SAL	DH,1			;  AND EVEN/ODD ROWS

;----- DETERMINE CRT MODE

	CMP	CRT_MODE,6		; TEST FOR MEDIUM RES
	JNC	R12			; FIND_SOURCE_DOWN

;----- MEDIUM RES DOWN

	SAL	DL,1			; # COLUMNS * 2, SINCE
					;  2 BYTES/CHAR (OFFSET OK)
	SAL	DI,1			; OFFSET *2 SINCE 2 BYTES/CHAR
	INC	DI			; POINT TO LAST BYTE

;----- DETERMINE THE SOURCE ADDRESS IN THE BUFFER

R12:					; FIND_SOURCE_DOWN
	PUSH	ES			; BOTH SEGMENTS TO REGEN
	POP	DS
	SUB	CH,CH			; ZERO TO HIGH OF COUNT REG
	ADD	DI,240			; POINT TO LAST ROW OF PIXELS
	SAL	BL,1			; MULTIPLY NUMBER OF LINES BY 4
	SAL	BL,1
	JZ	R16			; IF ZERO, THEN BLANK ENTIRE FIELD
	MOV	AL,BL			; GET NUMBER OF LINES IN AL
	MOV	AH,80			; 80 BYTES/ROW
	MUL	AH			; DETERMINE OFFSET TO SOURCE
	MOV	SI,DI			; SET UP SOURCE
	SUB	SI,AX			;  SUBTRACT THE OFFSET
	MOV	AH,DH			; NUMBER OF ROWS IN FIELD
	SUB	AH,BL			; DETERMINE NUMBER TO MOVE

;----- LOOP THROUGH, MOVING ONE ROW AT A TIME, BOTH EVEN AND ODD FIELDS

R13:					; ROW_LOOP_DOWN
	CALL	R17			; MOVE ONE ROW
	SUB	SI,2000H+80		; MOVE TO NEXT ROW
	SUB	DI,2000H+80
	DEC	AH			; NUMBER OF ROWS TO MOVE
	JNZ	R13			; CONTINUE TILL ALL MOVED

;----- FILL IN THE VACATED LINE(S)

R14:					; CLEAR_ENTRY_DOWN
	MOV	AL,BH			; ATTRIBUTE TO FILL WITH
R15:					; CLEAR_LOOP_DOWN
	CALL	R18			; CLEAR A ROW
	SUB	DI,2000H+80		; POINT TO NEXT LINE
	DEC	BL			; NUMBER OF LINES TO FILL
	JNZ	R15			; CLEAR_LOOP_DOWN
	CLD				; RESET THE DIRECTION FLAG
	JMP	VIDEO_RETURN		; EVERYTHING DONE
R16:					; BLANK_FIELD_DOWN
	MOV	BL,DH			; SET BLANK COST TO
					;  EVERYTHING IN FIELD
	JMP	R14			; CLEAR THE FIELD
GRAPHICS_DOWN	ENDP

;----- ROUTINE TO MOVE ONE ROW OF INFORMATION

R17	PROC	NEAR
	MOV	CL,DL			; NUMBER OF BYTES IN THE ROW
	PUSH	SI
	PUSH	DI			; SAVE POINTERS
	REP	MOVSB			; MOVE THE EVEN FIELD
	POP	DI
	POP	SI
	ADD	SI,2000H
	ADD	DI,2000H		; POINT TO THE ODD FIELD
	PUSH	SI
	PUSH	DI			; SAVE THE POINTERS
	MOV	CL,DL			; COUNT BACK
	REP	MOVSB			; MOVE THE ODD FIELD
	POP	DI
	POP	SI			; POINTERS BACK
	RET				; RETURN TO CALLER
R17	ENDP

;----- CLEAR A SINGLE ROW

R18	PROC	NEAR
	MOV	CL,DL			; NUMBER OF BYTES IN FIELD
	PUSH	DI			; SAVE POINTER
	REP	STOSB			; STORE THE NEW VALUE
	POP	DI			; POINTER BACK
	ADD	DI,2000H		; POINT TO ODD FIELD
	PUSH	DI
	MOV	CL,DL
	REP	STOSB			; FILL THE ODD FIELD
	POP	DI
	RET				; RETURN TO CALLER
R18	ENDP
;----------------------------------------------------------------
; GRAPHICS WRITE						:
;	THIS ROUTINE WRITES THE ASCII CHARACTER TO THE		:
;	CURRENT POSITION ON THE SCREEN. 			:
; ENTRY 							:
;	AL = CHARACTER TO WRITE 				:
;	BL = COLOR ATTRIBUTE TO BE USED FOR FOREGROUND COLOR	:
;	 IF BIT 7 IS SET, THE CHAR IS XOR'D INTO THE REGEN      :
;	 BUFFER (0 IS USED FOR THE BACKGROUND COLOR)		:
;	CX = NUMBER OF CHARS TO WRITE				:
;	DS = DATA SEGMENT					:
;	ES = REGEN SEGMENT					:
; EXIT								:
;	NOTHING IS RETURNED					:
;								:
; GRAPHICS READ 						:
;	THIS ROUTINE READS THE ASCII CHARACTER AT THE CURRENT	:
;	CURSOR POSITION ON THE SCREEN BY MATCHING THE DOTS ON	:
;	THE SCREEN TO THE CHARACTER GENERATOR CODE POINTS	:
; ENTRY 							:
;	NONE  (0 IS ASSUMED AS THE BACKGROUND COLOR		:
; EXIT								:
;	AL = CHARACTER READ AT THAT POSITION (0 RETURNED IF	:
;	     NONE FOUND)					:
;								:
; FOR BOTH ROUTINES, THE IMAGES USED TO FORM CHARS ARE		:
;  CONTAINED IN ROM FOR THE 1ST 128 CHARS.  TO ACCESS CHARS	:
;  IN THE SECOND HALF, THE USER MUST INITIALIZE THE VECTOR AT	:
;  INTERRUPT 1FH (LOCATION 0007CH) TO POINT TO THE USER 	:
;  SUPPLIED TABLE OF GRAPHIC IMAGES (8X8 BOXES).		:
;  FAILURE TO DO SO WILL CAUSE IN STRANGE RESULTS		:
;----------------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA,ES:DATA
GRAPHICS_WRITE	PROC	NEAR
	MOV	AH,0			; ZERO TO HIGH OF CODE POINT
	PUSH	AX			; SAVE CODE POINT VALUE

;----- DETERMINE POSITION IN REGEN BUFFER TO PUT CODE POINTS

	CALL	S26			; FIND LOCATION IN REGEN BUFFER
	MOV	DI,AX			; REGEN POINTER IN DI

;----- DETERMINE REGION TO GET CODE POINTS FROM

	POP	AX			; RECOVER CODE POINT
	CMP	AL,80H			; IS IT IN SECOND HALF
	JAE	S1			; YES

;----- IMAGE IS IN FIRST HALF, CONTAINED IN ROM

	MOV	SI,0FA6EH		; OFFSET CRT_CHAR_GEN-OFFSET OF IMAGES
	PUSH	CS			; SAVE SEGMENT ON STACK
	JMP	SHORT S2		; DETERMINE_MODE

;----- IMAGE IS IN SECOND HALF, IN USER RAM

S1:					; EXTEND_CHAR
	SUB	AL,80H			; ZERO ORIGIN FOR SECOND HALF
	PUSH	DS			; SAVE DATA POINTER
	SUB	SI,SI
	MOV	DS,SI			; ESTABLISH VECTOR ADDRESSING
	ASSUME	DS:ABS0
	LDS	SI,EXT_PTR		; GET THE OFFSET OF THE TABLE
	MOV	DX,DS			; GET THE SEGMENT OF THE TABLE
	ASSUME	DS:DATA
	POP	DS			; RECOVER DATA SEGMENT
	PUSH	DX			; SAVE TABLE SEGMENT ON STACK

;----- DETERMINE GRAPHICS MODE IN OPERATION

S2:					; DETERMINE_MODE
	SAL	AX,1			; MULTIPLY CODE POINT
	SAL	AX,1			;  VALUE BY 8
	SAL	AX,1
	ADD	SI,AX			; SI HAS OFFSET OF DESIRED CODES
	CMP	CRT_MODE,6
	POP	DS			; RECOVER TABLE POINTER SEGMENT
	JC	S7			; TEST FOR MEDIUM RESOLUTION MODE

;----- HIGH RESOLUTION MODE

S3:					; HIGH_CHAR
	PUSH	DI			; SAVE REGEN POINTER
	PUSH	SI			; SAVE CODE POINTER
	MOV	DH,4			; NUMBER OF LINES THROUGH LOOP
S4:
	LODSB				; GET BYTE FROM CODE POINTS
	TEST	BL,80H			; SHOULD WE USE THE FUNCTION
	JNZ	S6			;  TO PUT CHAR IN
	STOSB				; STORE IN REGEN BUFFER
	LODSB
S5:
	MOV	ES:[DI+2000H-1],AL	; STORE IN SECOND HALF
	ADD	DI,79			; MOVE TO NEXT ROW IN REGEN
	DEC	DH			; DONE WITH LOOP
	JNZ	S4
	POP	SI
	POP	DI			; RECOVER REGEN POINTER
	INC	DI			; POINT TO NEXT CHAR POSITION
	LOOP	S3			; MORE CHARS TO WRITE
	JMP	VIDEO_RETURN
S6:
	XOR	AL,ES:[DI]		; EXCLUSIVE OR WITH CURRENT
	STOSB				; STORE THE CODE POINT
	LODSB				; AGAIN FOR ODD FIELD
	XOR	AL,ES:[DI+2000H-1]
	JMP	S5			; BACK TO MAINSTREAM

;----- MEDIUM RESOLUTION WRITE

S7:					; MED_RES_WRITE
	MOV	DL,BL			; SAVE HIGH COLOR BIT
	SAL	DI,1			; OFFSET*2 SINCE 2 BYTES/CHAR
	CALL	S19			; EXPAND BL TO FULL WORD OF COLOR
S8:					; MED_CHAR
	PUSH	DI			; SAVE REGEN POINTER
	PUSH	SI			; SAVE THE CODE POINTER
	MOV	DH,4			; NUMBER OF LOOPS
S9:
	LODSB				; GET CODE POINT
	CALL	S21			; DOUBLE UP ALL THE BITS
	AND	AX,BX			; CONVERT THEM TO FOREGROUND
					;  COLOR ( 0 BACK )
	TEST	DL,80H			; IS THIS XOR FUNCTION
	JZ	S10			; NO, STORE IT IN AS IT IS
	XOR	AH,ES:[DI]		; DO FUNCTION WITH HALF
	XOR	AL,ES:[DI+1]		;  AND WITH OTHER HALF
S10:
	MOV	ES:[DI],AH		; STORE FIRST BYTE
	MOV	ES:[DI+1],AL		; STORE SECOND BYTE
	LODSB				; GET CODE POINT
	CALL	S21
	AND	AX,BX			; CONVERT TO COLOR
	TEST	DL,80H			; AGAIN, IS THIS XOR FUNCTION
	JZ	S11			; NO, JUST STORE THE VALUES
	XOR	AH,ES:[DI+2000H]	; FUNCTION WITH FIRST HALF
	XOR	AL,ES:[DI+2001H]	; AND WITH SECOND HALF
S11:
	MOV	ES:[DI+2000H],AH
	MOV	ES:[DI+2000H+1],AL	; STORE IN SECOND PORTION OF BUFFER
	ADD	DI,80			; POINT TO NEXT LOCATION
	DEC	DH
	JNZ	S9			; KEEP GOING
	POP	SI			; RECOVER CODE POINTER
	POP	DI			; RECOVER REGEN POINTER
	INC	DI			; POINT TO NEXT CHAR POSITION
	INC	DI
	LOOP	S8			; MORE TO WRITE
	JMP	VIDEO_RETURN
GRAPHICS_WRITE	ENDP
;------------------------
; GRAPHICS READ 	:
;------------------------
GRAPHICS_READ	PROC	NEAR
	CALL	S26			; CONVERTED TO OFFSET IN REGEN
	MOV	SI,AX			; SAVE IN SI
	SUB	SP,8			; ALLOCATE SPACE TO SAVE THE
					;  READ CODE POINTER
	MOV	BP,SP			; POINTER TO SAVE AREA

;----- DETERMINE GRAPHICS MODES

	CMP	CRT_MODE,6
	PUSH	ES
	POP	DS			; POINT TO REGEN SEGMENT
	JC	S13			; MEDIUM RESOLUTION

;----- HIGH RESOLUTION READ

;----- GET VALUES FROM REGEN BUFFER AND CONVERT TO CODE POINT

	MOV	DH,4			; NUMBER OF PASSES
S12:
	MOV	AL,[SI] 		; GET FIRST BYTE
	MOV	[BP],AL 		; SAVE IN STORAGE AREA
	INC	BP			; NEXT LOCATION
	MOV	AL,[SI+2000H]		; GET LOWER REGION BYTE
	MOV	[BP],AL 		; ADJUST AND STORE
	INC	BP
	ADD	SI,80			; POINTER INTO REGEN
	DEC	DH			; LOOP CONTROL
	JNZ	S12			; DO IT SOME MORE
	JMP	S15			; GO MATCH THE SAVED CODE POINTS

;----- MEDIUM RESOLUTION READ

S13:					; MED_RES_READ
	SAL	SI,1			; OFFSET*2 SINCE 2 BYTES/CHAR
	MOV	DH,4			; NUMBER OF PASSES
S14:
	CALL	S23			; GET PAIR BYTES FROM REGEN
					;  INTO SINGLE SAVE
	ADD	SI,2000H		; GO TO LOWER REGION
	CALL	S23			; GET THIS PAIR INTO SAVE
	SUB	SI,2000H-80		; ADJUST POINTER BACK INTO UPPER
	DEC	DH
	JNZ	S14			; KEEP GOING UNTIL ALL 8 DONE

;----- SAVE AREA HAS CHARACTER IN IT, MATCH IT

S15:					; FIND_CHAR
	MOV	DI,OFFSET CRT_CHAR_GEN	; ESTABLISH ADDRESSING
	PUSH	CS
	POP	ES			; CODE POINTS IN CS
	SUB	BP,8			; ADJUST POINTER TO BEGINNING
					;  OF SAVE AREA
	MOV	SI,BP
	CLD				; ENSURE DIRECTION
	MOV	AL,0			; CURRENT CODE POINT BEING MATCHED
S16:
	PUSH	SS			; ESTABLISH ADDRESSING TO STACK
	POP	DS			; FOR THE STRING COMPARE
	MOV	DX,128			; NUMBER TO TEST AGAINST
S17:
	PUSH	SI			; SAVE SAVE AREA POINTER
	PUSH	DI			; SAVE CODE POINTER
	MOV	CX,8			; NUMBER OF BYTES TO MATCH
	REPE	CMPSB			; COMPARE THE 8 BYTES
	POP	DI			; RECOVER THE POINTERS
	POP	SI
	JZ	S18			; IF ZERO FLAG SET, THEN MATCH OCCURRED
	INC	AL			; NO MATCH, MOVE ON TO NEXT
	ADD	DI,8			; NEXT CODE POINT
	DEC	DX			; LOOP CONTROL
	JNZ	S17			; DO ALL OF THEM

;----- CHAR NOT MATCHED, MIGHT BE IN USER SUPPLIED SECOND HALF

	CMP	AL,0			; AL <> 0 IF ONLY 1ST HALF SCANNED
	JE	S18			; IF = 0, THEN ALL HAS BEEN SCANNED
	SUB	AX,AX
	MOV	DS,AX			; ESTABLISH ADDRESSING TO VECTOR
	ASSUME	DS:ABS0
	LES	DI,EXT_PTR		; GET POINTER
	MOV	AX,ES			; SEE IF THE POINTER REALLY EXISTS
	OR	AX,DI			; IF ALL 0, THEN DOESN'T EXIST
	JZ	S18			; NO SENSE LOOKING
	MOV	AL,128			; ORIGIN FOR SECOND HALF
	JMP	S16			; GO BACK AND TRY FOR IT
	ASSUME	DS:DATA

;----- CHARACTER IS FOUND ( AL=0 IF NOT FOUND )

S18:
	ADD	SP,8			; READJUST THE STACK, THROW AWAY SAVE
	JMP	VIDEO_RETURN		; ALL DONE
GRAPHICS_READ	ENDP
;--------------------------------------------------------
; EXPAND_MED_COLOR					:
;	THIS ROUTINE EXPANDS THE LOW 2 BITS IN BL TO	:
;	FILL THE ENTIRE BX REGISTER			:
; ENTRY 						:
;	BL = COLOR TO BE USED ( LOW 2 BITS )		:
; EXIT							:
;	BX = COLOR TO BE USED ( 8 REPLICATIONS OF THE	:
;	2 COLOR BITS )					:
;--------------------------------------------------------
S19	PROC	NEAR
	AND	BL,3			; ISOLATE THE COLOR BITS
	MOV	AL,BL			; COPY TO AL
	PUSH	CX			; SAVE REGISTER
	MOV	CX,3			; NUMBER OF TIMES TO DO THIS
S20:
	SAL	AL,1
	SAL	AL,1			; LEFT SHIFT BY 2
	OR	BL,AL			; ANOTHER COLOR VERSION INTO BL
	LOOP	S20			; FILL ALL OF BL
	MOV	BH,BL			; FILL UPPER PORTION
	POP	CX			; REGISTER BACK
	RET				; ALL DONE
S19	ENDP
;--------------------------------------------------------
; EXPAND_BYTE						:
;	THIS ROUTINE TAKES THE BYTE IN AL AND DOUBLES	:
;	ALL OF THE BITS, TURNING THE 8 BITS INTO	:
;	16 BITS. THE RESULT IS LEFT IN AX		:
;--------------------------------------------------------
S21	PROC	NEAR
	PUSH	DX			; SAVE REGISTERS
	PUSH	CX
	PUSH	BX
	SUB	DX,DX			; RESULT REGISTER
	MOV	CX,1			; MASK REGISTER
S22:
	MOV	BX,AX			; BASE INTO TEMP
	AND	BX,CX			; USE MASK TO EXTRACT A BIT
	OR	DX,BX			; PUT INTO RESULT REGISTER
	SHL	AX,1
	SHL	CX,1			; SHIFT BASE AND MASK BY 1
	MOV	BX,AX			; BASE TO TEMP
	AND	BX,CX			; EXTRACT THE SAME BIT
	OR	DX,BX			; PUT INTO RESULT
	SHL	CX,1			; SHIFT ONLY MASK NOW,
					;  MOVING TO NEXT BASE
	JNC	S22			; USE MASK BIT COMING OUT TO TERMINATE
	MOV	AX,DX			; RESULT TO PARM REGISTER
	POP	BX
	POP	CX			; RECOVER REGISTERS
	POP	DX
	RET				; ALL DONE
S21	ENDP
;--------------------------------------------------------
; MED_READ_BYTE 					:
;	THIS ROUTINE WILL TAKE 2 BYTES FROM THE REGEN	:
;	BUFFER, COMPARE AGAINST THE CURRENT FOREGROUND	:
;	COLOR, AND PLACE THE CORRESPONDING ON/OFF BIT	:
;	PATTERN INTO THE CURRENT POSITION IN THE SAVE	:
;	AREA						:
; ENTRY 						:
;	SI,DS = POINTER TO REGEN AREA OF INTEREST	:
;	BX = EXPANDED FOREGROUND COLOR			:
;	BP = POINTER TO SAVE AREA			:
; EXIT							:
;	BP IS INCREMENT AFTER SAVE			:
;--------------------------------------------------------
S23	PROC	NEAR
	MOV	AH,[SI] 		; GET FIRST BYTE
	MOV	AL,[SI+1]		; GET SECOND BYTE
	MOV	CX,0C000H		; 2 BIT MASK TO TEST THE ENTRIES
	MOV	DL,0			; RESULT REGISTER
S24:
	TEST	AX,CX			; IS THIS SECTION BACKGROUND?
	CLC				; CLEAR CARRY IN HOPES THAT IT IS
	JZ	S25			; IF ZERO, IT IS BACKGROUND
	STC				; WASN'T, SO SET CARRY
S25:	RCL	DL,1			; MOVE THAT BIT INTO THE RESULT
	SHR	CX,1
	SHR	CX,1			; MOVE THE MASK TO THE RIGHT BY 2 BITS
	JNC	S24			; DO IT AGAIN IF MASK DIDN'T FALL OUT
	MOV	[BP],DL 		; STORE RESULT IN SAVE AREA
	INC	BP			; ADJUST POINTER
	RET				; ALL DONE
S23	ENDP
;------------------------------------------------
; V4_POSITION					:
;	THIS ROUTINE TAKES THE CURSOR POSITION	:
;	CONTAINED IN THE MEMORY LOCATION, AND	:
;	CONVERTS IT INTO AN OFFSET INTO THE	:
;	REGEN BUFFER, ASSUMING ONE BYTE/CHAR.	:
;	FOR MEDIUM RESOLUTION GRAPHICS, 	:
;	THE NUMBER MUST BE DOUBLED.		:
; ENTRY 					:
;	NO REGISTERS, MEMORY LOCATION		:
;	CURSOR_POSN IS USED			:
; EXIT						:
;	AX CONTAINS OFFSET INTO REGEN BUFFER	:
;------------------------------------------------
S26	PROC	NEAR
	MOV	AX,CURSOR_POSN		; GET CURRENT CURSOR
GRAPH_POSN	LABEL	NEAR
	PUSH	BX			; SAVE REGISTER
	MOV	BX,AX			; SAVE A COPY OF CURRENT CURSOR
	MOV	AL,AH			; GET ROWS TO AL
	MUL	BYTE PTR CRT_COLS	; MULTIPLY BY BYTES/COLUMN
	SHL	AX,1			; MULTIPLY * 4 SINCE 4 ROWS/BYTE
	SHL	AX,1
	SUB	BH,BH			; ISOLATE COLUMN VALUE
	ADD	AX,BX			; DETERMINE OFFSET
	POP	BX			; RECOVER POINTER
	RET				; ALL DONE
S26	ENDP
;------------------------------------------------------------------------
; WRITE_TTY								:
;	THIS INTERFACE PROVIDES A TELETYPE LIKE INTERFACE TO THE VIDEO	:
;	CARD. THE INPUT CHARACTER IS WRITTEN TO THE CURRENT CURSOR	:
;	POSITION, AND THE CURSOR IS MOVED TO THE NEXT POSITION. IF THE	:
;	CURSOR LEAVES THE LAST COLUMN OF THE FIELD, THE COLUMN IS SET	:
;	TO ZERO, AND THE ROW VALUE IS INCREMENTED. IF THE ROW VALUE	:
;	LEAVES THE FIELD, THE CURSOR IS PLACED ON THE LAST ROW, FIRST	:
;	COLUMN, AND THE ENTIRE SCREEN IS SCROLLED UP ONE LINE. WHEN	:
;	THE SCREEN IS SCROLLED UP, THE ATTRIBUTE FOR FILLING THE NEWLY	:
;	BLANKED LINE IS READ FROM THE CURSOR POSITION ON THE PREVIOUS	:
;	LINE BEFORE THE SCROLL, IN CHARACTER MODE. IN GRAPHICS MODE,	:
;	THE 0 COLOR IS USED.						:
; ENTRY 								:
;	(AH) = CURRENT CRT MODE 					:
;	(AL) = CHARACTER TO BE WRITTEN					:
;	 NOTE THAT BACK SPACE, CAR RET, BELL AND LINE FEED ARE HANDLED	:
;	 AS COMMANDS RATHER THAN AS DISPLAYABLE GRAPHICS		:
;	(BL) = FOREGROUND COLOR FOR CHAR WRITE IF CURRENTLY IN A	:
;	 GRAPHICS MODE							:
; EXIT									:
;	ALL REGISTERS SAVED						:
;------------------------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA
WRITE_TTY	PROC	NEAR
	PUSH	AX			; SAVE REGISTERS
	PUSH	AX			; SAVE CHAR TO WRITE
	MOV	AH,3
	MOV	BH,ACTIVE_PAGE		; GET THE CURRENT ACTIVE PAGE
	INT	10H			; READ THE CURRENT CURSOR POSITION
	POP	AX			; RECOVER CHAR

;----- DX NOW HAS THE CURRENT CURSOR POSITION

	CMP	AL,8			; IS IT A BACKSPACE
	JE	U8			; BACK_SPACE
	CMP	AL,0DH			; IS IT CARRIAGE RETURN
	JE	U9			; CAR_RET
	CMP	AL,0AH			; IS IT A LINE FEED
	JE	U10			; LINE_FEED
	CMP	AL,07H			; IS IT A BELL
	JE	U11			; BELL

;----- WRITE THE CHAR TO THE SCREEN


	MOV	AH,10			; WRITE CHAR ONLY
	MOV	CX,1			; ONLY ONE CHAR
	INT	10H			; WRITE THE CHAR

;----- POSITION THE CURSOR FOR NEXT CHAR

	INC	DL
	CMP	DL,BYTE PTR CRT_COLS	; TEST FOR COLUMN OVERFLOW
	JNZ	U7			; SET CURSOR
	MOV	DL,0			; COLUMN FOR CURSOR
	CMP	DH,24
	JNZ	U6			; SET_CURSOR_INC

;----- SCROLL REQUIRED

U1:
	MOV	AH,2
	INT	10H			; SET THE CURSOR

;----- DETERMINE VALUE TO FILL WITH DURING SCROLL

	MOV	AL,CRT_MODE		; GET THE CURRENT MODE
	CMP	AL,4
	JC	U2			; READ_CURSOR
	CMP	AL,7
	MOV	BH,0			; FILL WITH BACKGROUND
	JNE	U3			; SCROLL_UP
U2:					; READ_CURSOR
	MOV	AH,8
	INT	10H			; READ CHAR/ATTR AT CURRENT CURSOR
	MOV	BH,AH			; STORE IN BH
U3:					; SCROLL-UP
	MOV	AX,601H 		; SCROLL ONE LINE
	SUB	CX,CX			; UPPER LEFT CORNER
	MOV	DH,24			; LOWER RIGHT ROW
	MOV	DL,BYTE PTR CRT_COLS	; LOWER RIGHT COLUMN
	DEC	DL
U4:					; VIDEO-CALL-RETURN
	INT	10H			; SCROLL UP THE SCREEN
U5:					; TTY-RETURN
	POP	AX			; RESTORE THE CHARACTER
	JMP	VIDEO_RETURN		; RETURN TO CALLER
U6:					; SET-CURSOR-INC
	INC	DH			; NEXT ROW
U7:					; SET-CURSOR
	MOV	AH,2
	JMP	U4			; ESTABLISH THE NEW CURSOR

;----- BACK SPACE FOUND

U8:
	CMP	DL,0			; ALREADY AT END OF LINE
	JE	U7			; SET_CURSOR
	DEC	DL			; NO -- JUST MOVE IT BACK
	JMP	U7			; SET_CURSOR

;----- CARRIAGE RETURN FOUND

U9:
	MOV	DL,0			; MOVE TO FIRST COLUMN
	JMP	U7			; SET_CURSOR

;----- LINE FEED FOUND

U10:
	CMP	DH,24			; BOTTOM OF SCREEN
	JNE	U6			; YES, SCROLL THE SCREEN
	JMP	U1			; NO, JUST SET THE CURSOR

;----- BELL FOUND

U11:
	MOV	BL,2			; SET UP COUNT FOR BEEP
	CALL	BEEP			; SOUND THE POD BELL
	JMP	U5			; TTY_RETURN
WRITE_TTY	ENDP
;----------------------------------------------------------------
; LIGHT PEN							:
;	THIS ROUTINE TESTS THE LIGHT PEN SWITCH AND THE LIGHT	:
;	PEN TRIGGER. IF BOTH ARE SET, THE LOCATION OF THE LIGHT :
;	PEN IS DETERMINED.  OTHERWISE, A RETURN WITH NO 	:
;	INFORMATION IS MADE.					:
; ON EXIT							:
;	(AH) = 0 IF NO LIGHT PEN INFORMATION IS AVAILABLE	:
;		BX,CX,DX ARE DESTROYED				:
;	(AH) = 1 IF LIGHT PEN IS AVAILABLE			:
;		(DH,DL) = ROW,COLUMN OF CURRENT LIGHT PEN	:
;			  POSITION				:
;		(CH) = RASTER POSITION				:
;		(BX) = BEST GUESS AT PIXEL HORIZONTAL POSITION	:
;----------------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA
;------ SUBTRACT_TABLE
V1	LABEL	BYTE
	DB	3,3,5,5,3,3,3,4 ;
READ_LPEN	PROC	NEAR

;----- WAIT FOR LIGHT PEN TO BE DEPRESSED

	MOV	AH,0			; SET NO LIGHT PEN RETURN CODE
	MOV	DX,ADDR_6845		; GET BASE ADDRESS OF 6845
	ADD	DX,6			; POINT TO STATUS REGISTER
	IN	AL,DX			; GET STATUS REGISTER
	TEST	AL,4			; TEST LIGHT PEN SWITCH
	JNZ	V6			; NOT SET, RETURN

;----- NOW TEST FOR LIGHT PEN TRIGGER

	TEST	AL,2			; TEST LIGHT PEN TRIGGER
	JNZ	V7A			; RETURN WITHOUT RESETTING TRIGGER
	JMP	V7

;----- TRIGGER HAS BEEN SET, READ THE VALUE IN

V7A:
	MOV	AH,16			; LIGHT PEN REGISTERS IN 6845

;----- INPUT REGS POINTED TO BY AH, AND CONVERT TO ROW COLUMN IN DX

	MOV	DX,ADDR_6845		; ADDRESS REGISTER FOR 6845
	MOV	AL,AH			; REGISTER TO READ
	OUT	DX,AL			; SET IT UP
	INC	DX			; DATA REGISTER
	IN	AL,DX			; GET THE VALUE
	MOV	CH,AL			; SAVE IN CX
	DEC	DX			; ADDRESS REGISTER
	INC	AH
	MOV	AL,AH			; SECOND DATA REGISTER
	OUT	DX,AL
	INC	DX			; POINT TO DATA REGISTER
	IN	AL,DX			; GET SECOND DATA VALUE
	MOV	AH,CH			; AX HAS INPUT VALUE

;----- AX HAS THE VALUE READ IN FROM THE 6845

	MOV	BL,CRT_MODE
	SUB	BH,BH			; MODE VALUE TO BX
	MOV	BL,CS:V1[BX]		; DETERMINE AMOUNT TO SUBTRACT
	SUB	AX,BX			; TAKE IT AWAY
	MOV	BX,CRT_START
	SHR	BX,1
	SUB	AX,BX
	JNS	V2			; IF POSITIVE, DETERMINE MODE
	SUB	AX,AX			; <0 PLAYS AS 0

;----- DETERMINE MODE OF OPERATION

V2:					; DETERMINE_MODE
	MOV	CL,3			; SET *8 SHIFT COUNT
	CMP	CRT_MODE,4		; DETERMINE IF GRAPHICS OR ALPHA
	JB	V4			; ALPHA_PEN
	CMP	CRT_MODE,7
	JE	V4			; ALPHA_PEN

;----- GRAPHICS MODE

	MOV	DL,40			; DIVISOR FOR GRAPHICS
	DIV	DL			; DETERMINE ROW(AL) AND COLUMN(AH)
					;  AL RANGE 0-99, AH RANGE 0-39

;----- DETERMINE GRAPHIC ROW POSITION

	MOV	CH,AL			; SAVE ROW VALUE IN CH
	ADD	CH,CH			; *2 FOR EVEN/ODD FIELD
	MOV	BL,AH			; COLUMN VALUE TO BX
	SUB	BH,BH			; MULTIPLY BY 8 FOR MEDIUM RES
	CMP	CRT_MODE,6		; DETERMINE MEDIUM OR HIGH RES
	JNE	V3			; NOT_HIGH_RES
	MOV	CL,4			; SHIFT VALUE FOR HIGH RES
	SAL	AH,1			; COLUMN VALUE TIMES 2 FOR HIGH RES
V3:					; NOT_HIGH_RES
	SHL	BX,CL			; MULTIPLY *16 FOR HIGH RES

;----- DETERMINE ALPHA CHAR POSITION

	MOV	DL,AH			; COLUMN VALUE FOR RETURN
	MOV	DH,AL			; ROW VALUE
	SHR	DH,1			; DIVIDE BY 4
	SHR	DH,1			;  FOR VALUE IN 0-24 RANGE
	JMP	SHORT V5		; LIGHT_PEN_RETURN_SET

;------ ALPHA MODE ON LIGHT PEN

V4:					; ALPHA_PEN
	DIV	BYTE PTR CRT_COLS	; DETERMINE ROW,COLUMN VALUE
	MOV	DH,AL			; ROWS TO DH
	MOV	DL,AH			; COLS TO DL
	SAL	AL,CL			; MULTIPLY ROWS *8
	MOV	CH,AL			; GET RASTER VALUE TO RETURN REG
	MOV	BL,AH			; COLUMN VALUE
	XOR	BH,BH			; TO BX
	SAL	BX,CL
V5:					; LIGHT_PEN_RETURN_SET
	MOV	AH,1			; INDICATE EVERTHING SET
V6:					; LIGHT_PEN_RETURN
	PUSH	DX			; SAVE RETURN VALUE (IN CASE)
	MOV	DX,ADDR_6845		; GET BASE ADDRESS
	ADD	DX,7			; POINT TO RESET PARM
	OUT	DX,AL			; ADDRESS, NOT DATA, IS IMPORTANT
	POP	DX			; RECOVER VALUE
V7:					; RETURN_NO_RESET
	POP	DI
	POP	SI
	POP	DS			; DISCARD SAVED BX,CX,DX
	POP	DS
	POP	DS
	POP	DS
	POP	ES
	IRET
READ_LPEN	ENDP

;--- INT 12 -------------------------------------------------------------
; MEMORY_SIZE_DET							:
;	THIS ROUTINE DETERMINES THE AMOUNT OF MEMORY IN THE SYSTEM	:
;	AS REPRESENTED BY THE SWITCHES ON THE PLANAR. NOTE THAT THE	:
;	SYSTEM MAY NOT BE ABLE TO USE I/O MEMORY UNLESS THERE IS A FULL :
;	COMPLEMENT OF 64K BYTES ON THE PLANAR.				:
; INPUT 								:
;	NO REGISTERS							:
;	THE MEMORY_SIZE VARIABLE IS SET DURING POWER ON DIAGNOSTICS	:
;	 ACCORDING TO THE FOLLOWING HARDWARE ASSUMPTIONS:		:
;	PORT 60 BITS 3,2 = 00 - 16K BASE RAM				:
;			   01 - 32K BASE RAM				:
;			   10 - 48K BASE RAM				:
;			   11 - 64K BASE RAM				:
;	PORT 62 BITS 3-0 INDICATE AMOUNT OF I/O RAM IN 32K INCREMENTS	:
;		E.G., 0000 - NO RAM IN I/O CHANNEL			:
;		      0010 - 64K RAM IN I/O CHANNEL, ETC.		:
; OUTPUT								:
;	(AX) = NUMBER OF CONTIGUOUS 1K BLOCKS OF MEMORY 		:
;------------------------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA
	ORG	0F841H
MEMORY_SIZE_DET PROC	FAR
	STI				; INTERRUPTS BACK ON
	PUSH	DS			; SAVE SEGMENT
	CALL	DDS
	MOV	AX,MEMORY_SIZE		; GET VALUE
	POP	DS			; RECOVER SEGMENT
	IRET				; RETURN TO CALLER
MEMORY_SIZE_DET  ENDP

;--- INT 11 -----------------------------------------------------
; EQUIPMENT DETERMINATION					:
;	THIS ROUTINE ATTEMPTS TO DETERMINE WHAT OPTIONAL	:
;	DEVICES ARE ATTACHED TO THE SYSTEM.			:
; INPUT 							:
;	NO REGISTERS						:
;	THE EQUIP_FLAG VARIABLE IS SET DURING THE POWER ON	:
;	DIAGNOSTICS USING THE FOLLOWING HARDWARE ASSUMPTIONS:	:
;	PORT 60  = LOW ORDER BYTE OF EQUIPMENT			:
;	PORT 3FA = INTERRUPT ID REGISTER OF 8250		:
;		BITS 7-3 ARE ALWAYS 0				:
;	PORT 378 = OUTPUT PORT OF PRINTER -- 8255 PORT THAT	:
;		CAN BE READ AS WELL AS WRITTEN			:
; OUTPUT							:
;	(AX) IS SET, BIT SIGNIFICANT, TO INDICATE ATTACHED I/O	:
;	BIT 15,14 = NUMBER OF PRINTERS ATTACHED 		:
;	BIT 13 NOT USED 					:
;	BIT 12 = GAME I/O ATTACHED				:
;	BIT 11,10,9 = NUMBER OF RS232 CARDS ATTACHED		:
;	BIT 8 UNUSED						:
;	BIT 7,6 = NUMBER OF DISKETTE DRIVES			:
;		00=1, 01=2, 10-3, 11=4 ONLY IF BIT 0 = 1	:
;	BIT 5,4 = INITIAL VIDEO MODE				:
;			00 - UNUSED				:
;			01 - 40X25 BW USING COLOR CARD		:
;			10 - 80X25 BW USING COLOR CARD		:
;			11 - 80X25 BW USING BW CARD		:
;	BIT 3,2 = PLANAR RAM SIZE (00=16K,01=32K,10=48K,11=64K) :
;	BIT 1 NOT USED						:
;	BIT 0 = IPL FROM DISKETTE -- THIS BIT INDICATES THAT	:
;		THERE ARE DISKETTE DRIVES ON THE SYSTEM 	:
;								:
;	NO OTHER REGISTERS AFFECTED				:
;----------------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA
	ORG	0F84DH
EQUIPMENT	PROC	FAR
	STI			; INTERRUPTS BACK ON
	PUSH	DS		; SAVE SEGMENT REGISTER
	CALL	DDS
	MOV	AX,EQUIP_FLAG	; GET THE CURRENT SETTINGS
	POP	DS		; RECOVER SEGMENT
	IRET			; RETURN TO CALLER
EQUIPMENT	ENDP

;--- INT 15 -------------------------------------------------------------
;    DUMMY CASSETTE IO ROUTINE-RETURNS 'INVALID CMD' IF THE ROUTINE IS  :
;    IS EVER CALLED BY ACCIDENT (AH=86H, CARRY FLAG=1)			:
;------------------------------------------------------------------------
	ORG	0F859H
CASSETTE_IO	PROC	FAR
	STC			; CARRY INDICATOR=1
	MOV	AH,86H
	RET	2
CASSETTE_IO	ENDP

;----------------------------------------------------------------
; NON-MASKABLE INTERRUPT ROUTINE:				:
;	THIS ROUTINE WILL PRINT A PARITY CHECK 1 OR 2 MESSAGE	:
;	AND ATTEMPT TO FIND THE STORAGE LOCATION CONTAINING THE :
;	BAD PARITY.  IF FOUND, THE SEGMENT ADDRESS WILL BE	:
;	PRINTED.  IF NO PARITY ERROR CAN BE FOUND (INTERMITTANT :
;	READ PROBLEM) ?????<-WILL BE PRINTED WHERE THE ADDRESS	:
;	WOULD NORMALLY GO.					:
;	IF ADDRESS IN ERROR IS IN THE I/O EXPANSION BOX, THE	:
;	ADDRESS WILL BE FOLLOWED BY A '(E)', IF IN SYSTEM UNIT, :
;	A '(S)' WILL FOLLOW THE ADDRESS                         :
;----------------------------------------------------------------
NMI_INT PROC	NEAR
	ASSUME	DS:DATA
	PUSH	AX			; SAVE ORIG CONTENTS OF AX
	IN	AL,PORT_C
	TEST	AL,0C0H 		; PARITY CHECK?
	JNZ	NMI_1
	JMP	D14			; NO, EXIT FROM ROUTINE
NMI_1:
	MOV	DX,DATA
	MOV	DS,DX
	MOV	SI,OFFSET D1		; ADDR OF ERROR MSG
	TEST	AL,40H			; I/O PARITY CHECK?
	JNZ	D13			; DISPLAY ERROR MSG
	MOV	SI,OFFSET D2		; MUST BE PLANAR
D13:
	MOV	AH,0			; INIT AND SET MODE FOR VIDEO
	MOV	AL,CRT_MODE
	INT	10H			; CALL VIDEO_IO PROCEDURE
	CALL	P_MSG			; PRINT ERROR MSG

;----- SET IF LOCATION THAT CAUSED PARITY CHECK CAN BE FOUND

	MOV	AL,00H			; DISABLE TRAP
	OUT	0A0H,AL
	IN	AL,PORT_B
	OR	AL,00110000B		; TOGGLE PARITY CHECK ENABLES
	OUT	PORT_B,AL
	AND	AL,11001111B
	OUT	PORT_B,AL
	MOV	BX,MEMORY_SIZE		; GET MEMORY SIZE WORD
	CLD				; SET DIR FLAG TO INCREMENT
	SUB	DX,DX			; POINT DX AT START OF MEM
NMI_LOOP:
	MOV	DS,DX
	MOV	ES,DX
	MOV	CX,4000H		; SET FOR 16KB SCAN
	SUB	SI,SI			; SET SI TO BE RELATIVE TO
					; START OF ES
	REP	LODSB			; READ 16KB OF MEMORY
	IN	AL,PORT_C		; SEE IF PARITY CHECK HAPPENED
	AND	AL,11000000B
	JNZ	PRT_NMI 		; GO PRINT ADDRESS IF IT DID
	ADD	DX,0400H		; POINT TO NEXT 16K BLOCK
	SUB	BX,16D
	JNZ	NMI_LOOP
	MOV	SI,(OFFSET D2A) 	; PRINT ROW OF ????? IF PARITY
	CALL	P_MSG			; CHECK COULD NOT BE RE-CREATED
	CLI
	HLT				; HALT SYSTEM
PRT_NMI:
	MOV	DX,DS
	CALL	PRT_SEG 		; PRINT SEGMENT VALUE
	MOV	DX,0213H
	MOV	AL,00			; DISABLE EXPANSION BOX
	OUT	DX,AL			; (CAN'T WRITE TO MEM)
	MOV	AL,'('
	CALL	PRT_HEX
	MOV	AX,0A55AH
	MOV	CX,AX
	SUB	BX,BX
	MOV	[BX],AX 		; WRITE A WORD TO SEGMENT THAT
	NOP
	NOP
	MOV	AX,[BX] 		; HAD THE ERROR
	CMP	AX,CX			; IS IT THERE?
	JE	SYS_BOX_ERR		; YES- MUST BE SYS UNIT
	MOV	AL,'E'                  ; NO-MUST BE IN EXP. BOX
	CALL	PRT_HEX
	JMP	SHORT HLT_NMI
SYS_BOX_ERR:
	MOV	AL,'S'
	CALL	PRT_HEX
HLT_NMI:
	MOV	AL,')'
	CALL	PRT_HEX
	CLI
	HLT				; HALT SYSTEM
D14:
	POP	AX			; RESTORE ORIG CONTENTS OF AX
	IRET
NMI_INT ENDP

;----------------------------------------
;	ROS CHECKSUM SUBROUTINE 	:
;----------------------------------------
ROS_CHECKSUM	PROC	NEAR		; NEXT_ROS_MODULE
	MOV	CX,8192 		; NUMBER OF BYTES TO ADD
ROS_CHECKSUM_CNT:			; ENTRY FOR OPTIONAL ROS TEST
	XOR	AL,AL
C26:
	ADD	AL,DS:[BX]
	INC	BX			; POINT TO NEXT BYTE
	LOOP	C26			; ADD ALL BYTES IN ROS MODULE
	OR	AL,AL			; SUM = 0?
	RET
ROS_CHECKSUM	ENDP

;----------------------------------------
;	MESSAGE AREA FOR POST		:
;----------------------------------------
E0	DB	'101',13,10
E1	DB	' 201',13,10
F3A	DB	'ROM',13,10
F3C	DB	'1801',13,10
D1	DB	'PARITY CHECK 2',13,10
D2	DB	'PARITY CHECK 1',13,10
D2A	DB	'?????',13,10

;---------------------------------------------------------------------------
;	BLINK LED PROCEDURE FOR MFG RUN-IN TESTS
;	 IF LED IS ON, TURN IT OFF. IF OFF, TURN ON.
;---------------------------------------------------------------------------
BLINK_INT	PROC	NEAR
	STI
	PUSH	AX			; SAVE AX REG CONTENTS
	IN	AL,PORT_B		; READ CURRENT VAL OF PORT B
	MOV	AH,AL
	NOT	AL			; FLIP ALL BITS
	AND	AL,01000000B		; ISOLATE CONTROL BIT
	AND	AH,10111111B		; MASK OUT OF ORIGINAL VAL
	OR	AL,AH			; OR NEW CONTROL BIT IN
	OUT	PORT_B,AL
	MOV	AL,EOI
	OUT	INTA00,AL
	POP	AX			; RESTORE AX REG
	IRET
BLINK_INT	ENDP

;--------------------------------------------------------
; THIS ROUTINE CHECKSUMS OPTIONAL ROM MODULES AND	:
; IF CHECKSUM IS OK, CALLS INIT/TEST CODE IN MODULE	:
;--------------------------------------------------------

ROM_CHECK	PROC	NEAR
	MOV	AX,DATA 		; POINT ES TO DATA AREA
	MOV	ES,AX
	SUB	AH,AH			; ZERO OUT AH
	MOV	AL,[BX+2]		; GET LENGTH INDICATOR
	MOV	CL,09H			; MULTIPLY BY 512
	SHL	AX,CL
	MOV	CX,AX			; SET COUNT
	PUSH	CX			; SAVE COUNT
	MOV	CX,4			; ADJUST
	SHR	AX,CL
	ADD	DX,AX			; SET POINTER TO NEXT MODULE
	POP	CX			; RETRIVE COUNT
	CALL	ROS_CHECKSUM_CNT	; DO CHECKSUM
	JZ	ROM_CHECK_1
	CALL	ROM_ERR 		; POST CHECKSUM ERROR
	JMP	ROM_CHECK_END		; AND EXIT
ROM_CHECK_1:
	PUSH	DX			; SAVE POINTER
	MOV	ES:IO_ROM_INIT,0003H	; LOAD OFFSET
	MOV	ES:IO_ROM_SEG,DS	; LOAD SEGMENT
	CALL	DWORD PTR ES:IO_ROM_INIT	; CALL INIT./TEST ROUTINE
	POP	DX
ROM_CHECK_END:
	RET				; RETURN TO CALLER
ROM_CHECK	ENDP

;------------------------------------------------
; CONVERT AND PRINT ASCII CODE			:
;	AL MUST CONTAIN NUMBER TO BE CONVERTED. :
;	AX AND BX DESTROYED.			:
;------------------------------------------------
XPC_BYTE	PROC	NEAR
	PUSH	AX			; SAVE FOR LOW NYBBLE DISPLAY
	MOV	CL,4			; SHIFT COUNT
	SHR	AL,CL			; NYBBLE SWAP
	CALL	XLAT_PR 		; DO THE HIGH NIBBLE DISPLAY
	POP	AX			; RECOVER THE NIBBLE
	AND	AL,0FH			; ISOLATE TO LOW NIBBLE
					; FALL INTO NIBBLE CONVERSION
XLAT_PR PROC	NEAR			;  CONVERT 00-0F TO ASCII CHARACTER
	ADD	AL,090H 		; ADD FIRST CONVERSION FACTOR
	DAA				; ADJUST FOR NUMERIC AND ALPHA RANGE
	ADC	AL,040H 		; ADD CONVERSION AND ADJUST LOW NIBBLE
	DAA				; ADJUST HI NIBBLE TO ASCII RANGE
PRT_HEX PROC	NEAR
	MOV	AH,14			; DISPLAY CHARACTER IN AL
	MOV	BH,0
	INT	10H			; CALL VIDEO_IO
	RET
PRT_HEX ENDP
XLAT_PR ENDP
XPC_BYTE	ENDP

F4	LABEL	WORD			; PRINTER SOURCE TABLE
	DW	3BCH
	DW	378H
	DW	278H
F4E	LABEL	WORD

;--------------------------------------------------------
; THIS SUBROUTINE WILL PRINT A MESSAGE ON THE DISPLAY	:
;							:
;ENTRY REQUIREMENTS:					:
;	SI = OFFSET(ADDRESS) OF MESSAGE BUFFER		:
;	CX = MESSAGE BYTE COUNT 			:
;	MAXIMUM MESSAGE LENGTH IS 36 CHARACTERS 	:
;--------------------------------------------------------
E_MSG	PROC	NEAR
	MOV	BP,SI			; SET BP NON-ZERO TO FLAG ERR
	CALL	P_MSG			; PRINT MESSAGE
	PUSH	DS
	CALL	DDS
	MOV	AL,BYTE PTR EQUIP_FLAG	; LOOP/HALT ON ERROR
	AND	AL,01H			; SWITCH ON?
	JNZ	G12			; NO - RETURN
MFG_HALT:
	CLI				; YES -  HALT SYSTEM
	MOV	AL,89H
	OUT	CMD_PORT,AL
	MOV	AL,10000101B		; DISABLE KB
	OUT	PORT_B,AL
	MOV	AL,MFG_ERR_FLAG 	; RECOVER ERROR INDICATOR
	OUT	PORT_A,AL		; SET INTO 8255 REG
	HLT				; HALT SYS
G12:
	POP	DS			; WRITE_MSG:
	RET
E_MSG	ENDP

P_MSG	PROC	NEAR
G12A:
	MOV	AL,CS:[SI]		; PUT CHAR IN AL
	INC	SI			; POINT TO NEXT CHAR
	PUSH	AX			; SAVE PRINT CHAR
	CALL	PRT_HEX 		; CALL VIDEO_IO
	POP	AX			; RECOVER PRINT CHAR
	CMP	AL,10			; WAS IT LINE FEED?
	JNE	G12A				; NO,KEEP PRINTING STRING
	RET
P_MSG	ENDP

;--------------------------------------------------------
;	INITIAL RELIABILITY TEST -- SUBROUTINES 	:
;--------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA
;------------------------------------------------------------------------
; SUBROUTINES FOR POWER ON DIAGNOSTICS					:
;	THIS PROCEDURE WILL ISSUE ONE LONG TONE (3 SECS) AND ONE OR	:
;	MORE SHORT TONES (1 SEC) TO INDICATE A FAILURE ON THE PLANAR	:
;	BOARD, A BAD RAM MODULE, OR A PROBLEM WITH THE CRT.		:
;ENTRY PARAMETERS:							:
;	DH = NUMBER OF LONG TONES TO BEEP				:
;	DL = NUMBER OF SHORT TONES TO BEEP				:
;------------------------------------------------------------------------
ERR_BEEP  PROC	NEAR
	PUSHF				; SAVE FLAGS
	CLI				; DISABLE SYSTEM INTERRUPTS
	PUSH	DS			; SAVE DS REG CONTENTS
	CALL	DDS
	OR	DH,DH			; ANY LONG ONES TO BEEP
	JZ	G3			; NO, DO THE SHORT ONES
G1:					; LONG_BEEP:
	MOV	BL,6			; COUNTER FOR BEEPS
	CALL	BEEP			; DO THE BEEP
G2:
	LOOP	G2			; DELAY BETWEEN BEEPS
	DEC	DH			; ANY MORE TO DO
	JNZ	G1			; DO IT
	CMP	MFG_TST,1		; MFG TEST MODE?
	JNE	G3			; YES - CONTINUE BEEPING SPEAKER
	JMP	MFG_HALT		; STOP BLINKING LED
G3:					; SHORT_BEEP:
	MOV	BL,1			; COUNTER FOR A SHORT BEEP
	CALL	BEEP			; DO THE SOUND
G4:
	LOOP	G4			; DELAY BETWEEN BEEPS
	DEC	DL			; DONE WITH SHORTS
	JNZ	G3			; DO SOME MORE
G5:
	LOOP	G5			; LONG DELAY BEFORE RETURN
G6:
	LOOP	G6
	POP	DS			; RESTORE ORIG CONTENTS OF DS
	POPF				; RESTORE FLAGS TO ORIG SETTINGS
	RET				; RETURN TO CALLER
ERR_BEEP	ENDP

;----- ROUTINE TO SOUND BEEPER

BEEP	PROC	NEAR
	MOV	AL,10110110B		; SEL TIM 2,LSB,MSB,BINARY
	OUT	TIMER+3,AL		; WRITE THE TIMER MODE REG
	MOV	AX,533H 		; DIVISOR FOR 1000 HZ
	OUT	TIMER+2,AL		; WRITE TIMER 2 CNT - LSB
	MOV	AL,AH
	OUT	TIMER+2,AL		; WRITE TIMER 2 CNT - MSB
	IN	AL,PORT_B		; GET CURRENT SETTING OF PORT
	MOV	AH,AL			; SAVE THAT SETTING
	OR	AL,03			; TURN SPEAKER ON
	OUT	PORT_B,AL
	SUB	CX,CX			; SET CNT TO WAIT 500 MS
G7:
	LOOP	G7			; DELAY BEFORE TURNING OFF
	DEC	BL			; DELAY CNT EXPIRED?
	JNZ	G7			; NO - CONTINUE BEEPING SPK
	MOV	AL,AH			; RECOVER VALUE OF PORT
	OUT	PORT_B,AL
	RET				; RETURN TO CALLER
BEEP	ENDP

;------------------------------------------------------------------------
;	THIS PROCEDURE WILL SEND A SOFTWARE RESET TO THE KEYBOARD.	:
;	SCAN CODE 'AA' SHOULD BE RETURNED TO THE CPU.                   :
;------------------------------------------------------------------------
KBD_RESET	PROC	NEAR
	ASSUME	DS:ABS0
	MOV	AL,08H			; SET KBD CLK LINE LOW
	OUT	PORT_B,AL		; WRITE 8255 PORT B
	MOV	CX,10582		; HOLD KBD CLK LOW FOR 20 MS
G8:
	LOOP	G8			; LOOP FOR 20 MS
	MOV	AL,0C8H 		; SET CLK, ENABLE LINES HIGH
	OUT	PORT_B,AL
SP_TEST:				; ENTRY FOR MANUFACTURING TEST 2
	MOV	AL,48H			; SET KBD CLK HIGH, ENABLE LOW
	OUT	PORT_B,AL
	MOV	AL,0FDH 		; ENABLE KEYBOARD INTERRUPTS
	OUT	INTA01,AL		; WRITE 8255 IMR
	MOV	DATA_AREA[OFFSET INTR_FLAG],0	; RESET INTERRUPT INDICATOR
	STI				; ENABLE INTERRUPTS
	SUB	CX,CX			; SETUP INTERRUPT TIMEOUT CNT
G9:
	TEST	DATA_AREA[OFFSET INTR_FLAG],02H ; DID A KEYBOARD INTR OCCUR?
	JNZ	G10			; YES - READ SCAN CODE RETURNED
	LOOP	G9			; NO - LOOP TILL TIMEOUT
G10:
	IN	AL,PORT_A		; READ KEYBOARD SCAN CODE
	MOV	BL,AL			; SAVE SCAN CODE JUST READ
	MOV	AL,0C8H 		; CLEAR KEYBOARD
	OUT	PORT_B,AL
	RET				; RETURN TO CALLER
KBD_RESET	ENDP

DDS	PROC	NEAR
	PUSH	AX			; SAVE AX
	MOV	AX,DATA
	MOV	DS,AX			; SET SEGMENT
	POP	AX			; RESTORE AX
	RET
DDS	ENDP

;-----------------------------------------------------------------------
;	CHARACTER GENERATOR GRAPHICS FOR 320X200 AND 640X200 GRAPHICS
;-----------------------------------------------------------------------
	ORG	0FA6EH
CRT_CHAR_GEN	LABEL	BYTE
	DB	000H,000H,000H,000H,000H,000H,000H,000H ; D_00
	DB	07EH,081H,0A5H,081H,0BDH,099H,081H,07EH ; D_01
	DB	07EH,0FFH,0DBH,0FFH,0C3H,0E7H,0FFH,07EH ; D_02
	DB	06CH,0FEH,0FEH,0FEH,07CH,038H,010H,000H ; D_03
	DB	010H,038H,07CH,0FEH,07CH,038H,010H,000H ; D_04
	DB	038H,07CH,038H,0FEH,0FEH,07CH,038H,07CH ; D_05
	DB	010H,010H,038H,07CH,0FEH,07CH,038H,07CH ; D_06
	DB	000H,000H,018H,03CH,03CH,018H,000H,000H ; D_07
	DB	0FFH,0FFH,0E7H,0C3H,0C3H,0E7H,0FFH,0FFH ; D_08
	DB	000H,03CH,066H,042H,042H,066H,03CH,000H ; D_09
	DB	0FFH,0C3H,099H,0BDH,0BDH,099H,0C3H,0FFH ; D_0A
	DB	00FH,007H,00FH,07DH,0CCH,0CCH,0CCH,078H ; D_08
	DB	03CH,066H,066H,066H,03CH,018H,07EH,018H ; D_0C
	DB	03FH,033H,03FH,030H,030H,070H,0F0H,0E0H ; D_0D
	DB	07FH,063H,07FH,063H,063H,067H,0E6H,0C0H ; D_0E
	DB	099H,05AH,03CH,0E7H,0E7H,03CH,05AH,099H ; D_0F
	DB	080H,0E0H,0F8H,0FEH,0F8H,0E0H,080H,000H ; D_10
	DB	002H,00EH,03EH,0FEH,03EH,00EH,002H,000H ; D_11
	DB	018H,03CH,07EH,018H,018H,07EH,03CH,018H ; D_12
	DB	066H,066H,066H,066H,066H,000H,066H,000H ; D_13
	DB	07FH,0DBH,0DBH,07BH,01BH,01BH,01BH,000H ; D_14
	DB	03EH,063H,038H,06CH,06CH,038H,0CCH,078H ; D_15
	DB	000H,000H,000H,000H,07EH,07EH,07EH,000H ; D_16
	DB	018H,03CH,07EH,018H,07EH,03CH,018H,0FFH ; D_17
	DB	018H,03CH,07EH,018H,018H,018H,018H,000H ; D_18
	DB	018H,018H,018H,018H,07EH,03CH,018H,000H ; D_19
	DB	000H,018H,00CH,0FEH,00CH,018H,000H,000H ; D_1A
	DB	000H,030H,060H,0FEH,060H,030H,000H,000H ; D_1B
	DB	000H,000H,0C0H,0C0H,0C0H,0FEH,000H,000H ; D_1C
	DB	000H,024H,066H,0FFH,066H,024H,000H,000H ; D_1D
	DB	000H,018H,03CH,07EH,0FFH,0FFH,000H,000H ; D_1E
	DB	000H,0FFH,0FFH,07EH,03CH,018H,000H,000H ; D_1F
	DB	000H,000H,000H,000H,000H,000H,000H,000H ; SP D_20
	DB	030H,078H,078H,030H,030H,000H,030H,000H ; ! D_21
	DB	06CH,06CH,06CH,000H,000H,000H,000H,000H ; " D_22
	DB	06CH,06CH,0FEH,06CH,0FEH,06CH,06CH,000H ; # D_23
	DB	030H,07CH,0C0H,078H,00CH,0F8H,030H,000H ; $ D_24
	DB	000H,0C6H,0CCH,018H,030H,066H,0C6H,000H ; PER CENT D_25
	DB	038H,06CH,038H,076H,0DCH,0CCH,076H,000H ; & D_26
	DB	060H,060H,0C0H,000H,000H,000H,000H,000H ; ' D_27
	DB	018H,030H,060H,060H,060H,030H,018H,000H ; ( D_28
	DB	060H,030H,018H,018H,018H,030H,060H,000H ; ) D_29
	DB	000H,066H,03CH,0FFH,03CH,066H,000H,000H ; * D_2A
	DB	000H,030H,030H,0FCH,030H,030H,000H,000H ; + D_2B
	DB	000H,000H,000H,000H,000H,030H,030H,060H ; , D_2C
	DB	000H,000H,000H,0FCH,000H,000H,000H,000H ; - D_2D
	DB	000H,000H,000H,000H,000H,030H,030H,000H ; . D_2E
	DB	006H,00CH,018H,030H,060H,0C0H,080H,000H ; / D_2F
	DB	07CH,0C6H,0CEH,0DEH,0F6H,0E6H,07CH,000H ; 0 D_30
	DB	030H,070H,030H,030H,030H,030H,0FCH,000H ; 1 D_31
	DB	078H,0CCH,00CH,038H,060H,0CCH,0FCH,000H ; 2 D_32
	DB	078H,0CCH,00CH,038H,00CH,0CCH,078H,000H ; 3 D_33
	DB	01CH,03CH,06CH,0CCH,0FEH,00CH,01EH,000H ; 4 D_34
	DB	0FCH,0C0H,0F8H,00CH,00CH,0CCH,078H,000H ; 5 D_35
	DB	038H,060H,0C0H,0F8H,0CCH,0CCH,078H,000H ; 6 D_36
	DB	0FCH,0CCH,00CH,018H,030H,030H,030H,000H ; 7 D_37
	DB	078H,0CCH,0CCH,078H,0CCH,0CCH,078H,000H ; 8 D_38
	DB	078H,0CCH,0CCH,07CH,00CH,018H,070H,000H ; 9 D_39
	DB	000H,030H,030H,000H,000H,030H,030H,000H ; : D_3A
	DB	000H,030H,030H,000H,000H,030H,030H,060H ; ; D_3B
	DB	018H,030H,060H,0C0H,060H,030H,018H,000H ; < D_3C
	DB	000H,000H,0FCH,000H,000H,0FCH,000H,000H ; = D_3D
	DB	060H,030H,018H,00CH,018H,030H,060H,000H ; > D_3E
	DB	078H,0CCH,00CH,018H,030H,000H,030H,000H ; ? D_3F
	DB	07CH,0C6H,0DEH,0DEH,0DEH,0C0H,078H,000H ; @ D_40
	DB	030H,078H,0CCH,0CCH,0FCH,0CCH,0CCH,000H ; A D_41
	DB	0FCH,066H,066H,07CH,066H,066H,0FCH,000H ; B D_42
	DB	03CH,066H,0C0H,0C0H,0C0H,066H,03CH,000H ; C D_43
	DB	0F8H,06CH,066H,066H,066H,06CH,0F8H,000H ; D D_44
	DB	0FEH,062H,068H,078H,068H,062H,0FEH,000H ; E D_45
	DB	0FEH,062H,068H,078H,068H,060H,0F0H,000H ; F D_46
	DB	03CH,066H,0C0H,0C0H,0CEH,066H,03EH,000H ; G D_47
	DB	0CCH,0CCH,0CCH,0FCH,0CCH,0CCH,0CCH,000H ; H D_48
	DB	078H,030H,030H,030H,030H,030H,078H,000H ; I D_49
	DB	01EH,00CH,00CH,00CH,0CCH,0CCH,078H,000H ; J D_4A
	DB	0E6H,066H,06CH,078H,06CH,066H,0E6H,000H ; K D_4B
	DB	0F0H,060H,060H,060H,062H,066H,0FEH,000H ; L D_4C
	DB	0C6H,0EEH,0FEH,0FEH,0D6H,0C6H,0C6H,000H ; M D_4C
	DB	0C6H,0E6H,0F6H,0DEH,0CEH,0C6H,0C6H,000H ; N D_4E
	DB	038H,06CH,0C6H,0C6H,0C6H,06CH,038H,000H ; O D_4F
	DB	0FCH,066H,066H,07CH,060H,060H,0F0H,000H ; P D_50
	DB	078H,0CCH,0CCH,0CCH,0DCH,078H,01CH,000H ; Q D_51
	DB	0FCH,066H,066H,07CH,06CH,066H,0E6H,000H ; R D_52
	DB	078H,0CCH,0E0H,070H,01CH,0CCH,078H,000H ; S D_53
	DB	0FCH,0B4H,030H,030H,030H,030H,078H,000H ; T D_54
	DB	0CCH,0CCH,0CCH,0CCH,0CCH,0CCH,0FCH,000H ; U D_55
	DB	0CCH,0CCH,0CCH,0CCH,0CCH,078H,030H,000H ; V D_56
	DB	0C6H,0C6H,0C6H,0D6H,0FEH,0EEH,0C6H,000H ; W D_57
	DB	0C6H,0C6H,06CH,038H,038H,06CH,0C6H,000H ; X D_58
	DB	0CCH,0CCH,0CCH,078H,030H,030H,078H,000H ; Y D_59
	DB	0FEH,0C6H,08CH,018H,032H,066H,0FEH,000H ; Z D_5A
	DB	078H,060H,060H,060H,060H,060H,078H,000H ; [ D_5B
	DB	0C0H,060H,030H,018H,00CH,006H,002H,000H ; BACKSLASH D_5C
	DB	078H,018H,018H,018H,018H,018H,078H,000H ; ] D_5D
	DB	010H,038H,06CH,0C6H,000H,000H,000H,000H ; CIRCUMFLEX D_5E
	DB	000H,000H,000H,000H,000H,000H,000H,0FFH ; _ D_5F
	DB	030H,030H,018H,000H,000H,000H,000H,000H ;   D_60
	DB	000H,000H,078H,00CH,07CH,0CCH,076H,000H ; LOWER CASE A D_61
	DB	0E0H,060H,060H,07CH,066H,066H,0DCH,000H ; L.C. B D_62
	DB	000H,000H,078H,0CCH,0C0H,0CCH,078H,000H ; L.C. C D_63
	DB	01CH,00CH,00CH,07CH,0CCH,0CCH,076H,000H ; L.C. D D_64
	DB	000H,000H,078H,0CCH,0FCH,0C0H,078H,000H ; L.C. E D_65
	DB	038H,06CH,060H,0F0H,060H,060H,0F0H,000H ; L.C. F D_66
	DB	000H,000H,076H,0CCH,0CCH,07CH,00CH,0F8H ; L.C. G D_67
	DB	0E0H,060H,06CH,076H,066H,066H,0E6H,000H ; L.C. H D_68
	DB	030H,000H,070H,030H,030H,030H,078H,000H ; L.C. I D_69
	DB	00CH,000H,00CH,00CH,00CH,0CCH,0CCH,078H ; L.C. J D_6A
	DB	0E0H,060H,066H,06CH,078H,06CH,0E6H,000H ; L.C. K D_6B
	DB	070H,030H,030H,030H,030H,030H,078H,000H ; L.C. L D_6C
	DB	000H,000H,0CCH,0FEH,0FEH,0D6H,0C6H,000H ; L.C. M D_6D
	DB	000H,000H,0F8H,0CCH,0CCH,0CCH,0CCH,000H ; L.C. N D_6E
	DB	000H,000H,078H,0CCH,0CCH,0CCH,078H,000H ; L.C. O D_6F
	DB	000H,000H,0DCH,066H,066H,07CH,060H,0F0H ; L.C. P D_70
	DB	000H,000H,076H,0CCH,0CCH,07CH,00CH,01EH ; L.C. Q D_71
	DB	000H,000H,0DCH,076H,066H,060H,0F0H,000H ; L.C. R D_72
	DB	000H,000H,07CH,0C0H,078H,00CH,0F8H,000H ; L.C. S D_73
	DB	010H,030H,07CH,030H,030H,034H,018H,000H ; L.C. T D_74
	DB	000H,000H,0CCH,0CCH,0CCH,0CCH,076H,000H ; L.C. U D_75
	DB	000H,000H,0CCH,0CCH,0CCH,078H,030H,000H ; L.C. V D_76
	DB	000H,000H,0C6H,0D6H,0FEH,0FEH,06CH,000H ; L.C. W D_77
	DB	000H,000H,0C6H,06CH,038H,06CH,0C6H,000H ; L.C. X D_78
	DB	000H,000H,0CCH,0CCH,0CCH,07CH,00CH,0F8H ; L.C. Y D_79
	DB	000H,000H,0FCH,098H,030H,064H,0FCH,000H ; L.C. Z D_7A
	DB	01CH,030H,030H,0E0H,030H,030H,01CH,000H ; { D_7B
	DB	018H,018H,018H,000H,018H,018H,018H,000H ; | D_7C
	DB	0E0H,030H,030H,01CH,030H,030H,0E0H,000H ; } D_7D
	DB	076H,0DCH,000H,000H,000H,000H,000H,000H ; TILDE D_7E
	DB	000H,010H,038H,06CH,0C6H,0C6H,0FEH,000H ; DELTA D_7F

;--- INT 1A ---------------------------------------------
; TIME_OF_DAY						:
;  THIS ROUTINE ALLOWS THE CLOCK TO BE SET/READ 	:
;							:
; INPUT 						:
;   (AH) = 0	READ THE CURRENT CLOCK SETTING		:
;		RETURNS CX = HIGH PORTION OF COUNT	:
;			DX = LOW PORTION OF COUNT	:
;			AL = 0 IF TIMER HAS NOT PASSED	:
;			 24 HOURS SINCE LAST READ	:
;			   <>0 IF ON ANOTHER DAY	:
;   (AH) = 1	SET THE CURRENT CLOCK			:
;	CX = HIGH PORTION OF COUNT			:
;	DX = LOW PORTION OF COUNT			:
; NOTE: COUNTS OCCUR AT THE RATE OF			:
;	 1193180/65536 COUNTS/SEC			:
;	(OR ABOUT 18.2 PER SECOND -- SEE EQUATES BELOW) :
;--------------------------------------------------------
	ASSUME	CS:CODE,DS:DATA
	ORG	0FE6EH
TIME_OF_DAY	PROC	FAR
	STI				; INTERRUPTS BACK ON
	PUSH	DS			; SAVE SEGMENT
	CALL	DDS
	OR	AH,AH			; AH=0
	JZ	T2			; READ_TIME
	DEC	AH			; AH=1
	JZ	T3			; SET_TIME
T1:					; TOD_RETURN
	STI				; INTERRUPTS BACK ON
	POP	DS			; RECOVER SEGMENT
	IRET				; RETURN TO CALLER
T2:					; READ_TIME
	CLI				; NO TIMER INTERRUPTS WHILE READING
	MOV	AL,TIMER_OFL
	MOV	TIMER_OFL,0		; GET OVERFLOW, AND RESET THE FLAG
	MOV	CX,TIMER_HIGH
	MOV	DX,TIMER_LOW
	JMP	T1			; TOD_RETURN
T3:					; SET_TIME
	CLI				; NO INTERRUPTS WHILE WRITING
	MOV	TIMER_LOW,DX
	MOV	TIMER_HIGH,CX		; SET THE TIME
	MOV	TIMER_OFL,0		; RESET OVERFLOW
	JMP	T1			; TOD_RETURN
TIME_OF_DAY	ENDP

;--------------------------------------------------------
; THIS ROUTINE HANDLES THE TIMER INTERRUPT FROM 	:
;  CHANNEL 0 OF THE 8253 TIMER. INPUT FREQUENCY 	:
;  IS 1.19318 MHZ AND THE DIVISOR IS 65536, RESULTING	:
;  IN APPROX. 18.2 INTERRUPTS EVERY SECOND.		:
;							:
; THE INTERRUPT HANDLER MAINTAINS A COUNT OF INTERRUPTS :
;  SINCE POWER ON TIME, WHICH MAY BE USED TO ESTABLISH	:
;  TIME OF DAY. 					:
; THE INTERRUPT HANDLER ALSO DECREMENTS THE MOTOR	:
;  CONTROL COUNT OF THE DISKETTE, AND WHEN IT EXPIRES,	:
;  WILL TURN OFF THE DISKETTE MOTOR, AND RESET THE	:
;  MOTOR RUNNING FLAGS. 				:
; THE INTERRUPT HANDLER WILL ALSO INVOKE A USER ROUTINE :
;  THROUGH INTERRUPT 1CH AT EVERY TIME TICK.  THE USER	:
;  MUST CODE A ROUTINE AND PLACE THE CORRECT ADDRESS IN :
;  THE VECTOR TABLE.					:
;--------------------------------------------------------
	ORG	0FEA5H
TIMER_INT	PROC	FAR
	STI				; INTERRUPTS BACK ON
	PUSH	DS
	PUSH	AX
	PUSH	DX			; SAVE MACHINE STATE
	CALL	DDS
	INC	TIMER_LOW		; INCREMENT TIME
	JNZ	T4			; TEST_DAY
	INC	TIMER_HIGH		; INCREMENT HIGH WORD OF TIME
T4:					; TEST_DAY
	CMP	TIMER_HIGH,018H 	; TEST FOR COUNT EQUALLING 24 HOURS
	JNZ	T5			; DISKETTE_CTL
	CMP	TIMER_LOW,0B0H
	JNZ	T5			; DISKETTE_CTL

;----- TIMER HAS GONE 24 HOURS

	SUB	AX,AX
	MOV	TIMER_HIGH,AX
	MOV	TIMER_LOW,AX
	MOV	TIMER_OFL,1

;----- TEST FOR DISKETTE TIME OUT

T5:					; DISKETTE_CTL
	DEC	MOTOR_COUNT
	JNZ	T6			; RETURN IF COUNT NOT OUT
	AND	MOTOR_STATUS,0F0H	; TURN OFF MOTOR RUNNING BITS
	MOV	AL,0CH
	MOV	DX,03F2H		; FDC CTL PORT
	OUT	DX,AL			; TURN OFF THE MOTOR
T6:					; TIMER_RET:
	INT	1CH			; TRANSFER CONTROL TO A USER ROUTINE
	MOV	AL,EOI
	OUT	020H,AL 		; END OF INTERRUPT TO 8259
	POP	DX
	POP	AX
	POP	DS			; RESET MACHINE STATE
	IRET				; RETURN FROM INTERRUPT
TIMER_INT	 ENDP

;------------------------------------------------
; THESE ARE THE VECTORS WHICH ARE MOVED INTO	:
; THE 8086 INTERRUPT AREA DURING POWER ON.	:
; ONLY THE OFFSETS ARE DISPLAYED HERE, CODE	:
; SEGMENT WILL BE ADDED FOR ALL OF THEM, EXCEPT :
; WHERE NOTED.					:
;------------------------------------------------
	ASSUME	CS:CODE
	ORG	0FEF3H
VECTOR_TABLE	LABEL	WORD		; VECTOR TABLE FOR MOVE TO INTERRUPTS
	DW	OFFSET TIMER_INT	; INTERRUPT 8
	DW	OFFSET	KB_INT		; INTERRUPT 9
	DW	OFFSET	D11		; INTERRUPT A
	DW	OFFSET	D11		; INTERRUPT B
	DW	OFFSET	D11		; INTERRUPT C
	DW	OFFSET	D11		; INTERRUPT D
	DW	OFFSET DISK_INT 	; INTERRUPT E
	DW	OFFSET	D11		; INTERRUPT F
	DW	OFFSET VIDEO_IO 	; INTERRUPT 10H
	DW	OFFSET EQUIPMENT	; INTERRUPT 11H
	DW	OFFSET MEMORY_SIZE_DET	; INTERRUPT 12H
	DW	OFFSET DISKETTE_IO	; INTERRUPT 13H
	DW	OFFSET RS232_IO 	; INTERRUPT 14H
	DW	OFFSET CASSETTE_IO	; INTERRUPT 15H
	DW	OFFSET KEYBOARD_IO	; INTERRUPT 16H
	DW	OFFSET PRINTER_IO	; INTERRUPT 17H

	DW	00000H			; INTERRUPT 18H
;	DW	0F600H			;  MUST BE INSERTED INTO TABLE LATER

	DW	OFFSET BOOT_STRAP	; INTERRUPT 19H
	DW	TIME_OF_DAY		; INTERRUPT 1AH -- TIME OF DAY
	DW	DUMMY_RETURN		; INTERRUPT 1BH -- KEYBOARD BREAK ADDR
	DW	DUMMY_RETURN		; INTERRUPT 1C -- TIMER BREAK ADDR
	DW	VIDEO_PARMS		; INTERRUPT 1D -- VIDEO PARAMETERS
	DW	OFFSET DISK_BASE	; INTERRUPT 1E -- DISK PARMS
	DW	0			; INTERRUPT 1F -- POINTER TO VIDEO EXT

;--------------------------------------------------------
; TEMPORARY INTERRUPT SERVICE ROUTINE			:
;	1. THIS ROUTINE IS ALSO LEFT IN PLACE AFTER THE :
;	POWER ON DIAGNOSTICS TO SERVICE UNUSED		:
;	INTERRUPT VECTORS. LOCATION 'INTR_FLAG' WILL    :
;	CONTAIN EITHER: 1. LEVEL OF HARDWARE INT. THAT	:
;	CAUSED CODE TO BE EXEC. 			:
;	2. 'FF' FOR NON-HARDWARE INTERUPTS THAT WAS     :
;	EXECUTED ACCIDENTLY.				:
;--------------------------------------------------------
D11	PROC	NEAR
	ASSUME	DS:DATA
	PUSH	DS
	PUSH	DX
	PUSH	AX			; SAVE REG AX CONTENTS
	CALL	DDS
	MOV	AL,0BH			; READ IN-SERVICE REG
	OUT	INTA00,AL		; (FIND OUT WHAT LEVEL BEING
	NOP				; SERVICED)
	IN	AL,INTA00		; GET LEVEL
	MOV	AH,AL			; SAVE IT
	OR	AL,AH			; 00? (NO HARDWARE ISR ACTIVE)
	JNZ	HW_INT
	MOV	AH,0FFH
	JMP	SHORT SET_INTR_FLAG	; SET FLAG TO FF IF NON-HDWARE
HW_INT:
	IN	AL,INTA01		; GET MASK VALUE
	OR	AL,AH			; MASK OFF LVL BEING SERVICED
	OUT	INTA01,AL
	MOV	AL,EOI
	OUT	INTA00,AL
SET_INTR_FLAG:
	MOV	INTR_FLAG,AH		; SET FLAG
	POP	AX			; RESTORE REG AX CONTENTS
	POP	DX
	POP	DS
DUMMY_RETURN:				; NEED IRET FOR VECTOR TABLE
	IRET
D11	ENDP

;------------------------------------------------
; DUMMY RETURN FOR ADDRESS COMPATIBILITY	:
;------------------------------------------------
	ORG	0FF53H
	IRET

;-- INT 5 ---------------------------------------------------------------
;	THIS LOGIC WILL BE INVOKED BY INTERRUPT 05H TO PRINT THE	:
;	SCREEN. THE CURSOR POSITION AT THE TIME THIS ROUTINE IS INVOKED :
;	WILL BE SAVED AND RESTORED UPON COMPLETION. THE ROUTINE IS	:
;	INTENDED TO RUN WITH INTERRUPTS ENABLED. IF A SUBSEQUENT	:
;	'PRINT SCREEN' KEY IS DEPRESSED DURING THE TIME THIS ROUTINE    :
;	IS PRINTING IT WILL BE IGNORED. 				:
;	ADDRESS 50:0 CONTAINS THE STATUS OF THE PRINT SCREEN:		:
;									:
;	50:0	=0	EITHER PRINT SCREEN HAS NOT BEEN CALLED 	:
;			OR UPON RETURN FROM A CALL THIS INDICATES	:
;			A SUCCESSFUL OPERATION. 			:
;		=1	PRINT SCREEN IS IN PROGRESS			:
;		=255	ERROR ENCOUNTERED DURING PRINTING		:
;------------------------------------------------------------------------
	ASSUME	CS:CODE,DS:XXDATA
	ORG	0FF54H
PRINT_SCREEN	PROC	FAR
	STI				; MUST RUN WITH INTERRUPTS ENABLED
	PUSH	DS			; MUST USE 50:0 FOR DATA AREA STORAGE
	PUSH	AX
	PUSH	BX
	PUSH	CX			; WILL USE THIS LATER FOR CURSOR LIMITS
	PUSH	DX			; WILL HOLD CURRENT CURSOR POSITION
	MOV	AX,XXDATA		; HEX 50
	MOV	DS,AX
	CMP	STATUS_BYTE,1		; SEE IF PRINT ALREADY IN PROGRESS
	JZ	EXIT			; JUMP IF PRINT ALREADY IN PROGRESS
	MOV	STATUS_BYTE,1		; INDICATE PRINT NOW IN PROGRESS
	MOV	AH,15			; WILL REQUEST THE CURRENT SCREEN MODE
	INT	10H			;	[AL]=MODE
					;	[AH]=NUMBER COLUMNS/LINE
					;	[BH]=VISUAL PAGE
;----------------------------------------------------------------
;	AT THIS POINT WE KNOW THE COLUMNS/LINE ARE IN		:
;	[AX] AND THE PAGE IF APPLICABLE IS IN[BH]. THE STACK   :
;	HAS DS,AX,BX,CX,DX PUSHED. [AL] HAS VIDEO MODE		:
;----------------------------------------------------------------
	MOV	CL,AH			; WILL MAKE USE OF [CX] REGISTER TO
	MOV	CH,25			; CONTROL ROW & COLUMNS
	CALL	CRLF			; CARRIAGE RETURN LINE FEED ROUTINE
	PUSH	CX			; SAVE SCREEN BOUNDS
	MOV	AH,3			; WILL NOW READ THE CURSOR.
	INT	10H			; AND PRESERVE THE POSITION
	POP	CX			; RECALL SCREEN BOUNDS
	PUSH	DX			; RECALL [BH]=VISUAL PAGE
	XOR	DX,DX			; WILL SET CURSOR POSITION TO [0,0]
;----------------------------------------------------------------
;	THE LOOP FROM PRI10 TO THE INSTRUCTION PRIOR TO PRI20	:
;	IS THE LOOP TO READ EACH CURSOR POSITION FROM THE	:
;	SCREEN AND PRINT.					:
;----------------------------------------------------------------
PRI10:
	MOV	AH,2			; TO INDICATE CURSOR SET REQUEST
	INT	10H			; NEW CURSOR POSITION ESTABLISHED
	MOV	AH,8			; TO INDICATE READ CHARACTER
	INT	10H			; CHARACTER NOW IN [AL]
	OR	AL,AL			; SEE IF VALID CHAR
	JNZ	PRI15			; JUMP IF VALID CHAR
	MOV	AL,' '                  ; MAKE A BLANK
PRI15:
	PUSH	DX			; SAVE CURSOR POSITION
	XOR	DX,DX			; INDICATE PRINTER 1
	XOR	AH,AH			; TO INDICATE PRINT CHAR IN [AL]
	INT	17H			; PRINT THE CHARACTER
	POP	DX			; RECALL CURSOR POSITION
	TEST	AH, 25H 		; TEST FOR PRINTER ERROR
	JNZ	ERR10			; JUMP IF ERROR DETECTED
	INC	DL			; ADVANCE TO NEXT COLUMN
	CMP	CL,DL			; SEE IF AT END OF LINE
	JNZ	PRI10			; IF NOT PROCEED
	XOR	DL,DL			; BACK TO COLUMN 0
	MOV	AH,DL			; [AH]=0
	PUSH	DX			; SAVE NEW CURSOR POSITION
	CALL	CRLF			; LINE FEED CARRIAGE RETURN
	POP	DX			; RECALL CURSOR POSITION
	INC	DH			; ADVANCE TO NEXT LINE
	CMP	CH,DH			; FINISHED?
	JNZ	PRI10			; IF NOT CONTINUE
PRI20:
	POP	DX			; RECALL CURSOR POSITION
	MOV	AH,2			; TO INDICATE CURSOR SET REQUEST
	INT	10H			; CURSOR POSITION RESTORED
	MOV	STATUS_BYTE,0		; INDICATE FINISHED
	JMP	SHORT EXIT		; EXIT THE ROUTINE
ERR10:
	POP	DX			; GET CURSOR POSITION
	MOV	AH,2			; TO REQUEST CURSOR SET
	INT	10H			; CURSOR POSITION RESTORED
ERR20:
	MOV	STATUS_BYTE,0FFH	; INDICATE ERROR
EXIT:
	POP	DX			; RESTORE ALL THE REGISTERS USED
	POP	CX
	POP	BX
	POP	AX
	POP	DS
	IRET
PRINT_SCREEN	ENDP

;------ CARRIAGE RETURN, LINE FEED SUBROUTINE

CRLF	PROC	NEAR
	XOR	DX,DX			; PRINTER 0
	XOR	AH,AH			; WILL NOW SEND INITIAL LF,CR
					;  TO PRINTER
	MOV	AL,12Q			; LF
	INT	17H			; SEND THE LINE FEED
	XOR	AH,AH			; NOW FOR THE CR
	MOV	AL,15Q			; CR
	INT	17H			; SEND THE CARRIAGE RETURN
	RET
CRLF	ENDP

;---------------------------------------------------------------
;	PRINT A SEGMENT VALUE TO LOOK LIKE A 20 BIT ADDRESS	:
;	DX MUST CONTAIN SEGMENT VALUE TO BE PRINTED		:
;---------------------------------------------------------------
PRT_SEG PROC	NEAR
	MOV	AL,DH			; GET MSB
	CALL	XPC_BYTE
	MOV	AL,DL			; LSB
	CALL	XPC_BYTE
	MOV	AL,'0'                  ;  PRINT A '0 '
	CALL	PRT_HEX
	MOV	AL,' '                  ;SPACE
	CALL	PRT_HEX
	RET
PRT_SEG ENDP

CODE	ENDS

;----------------------------------------
;	POWER ON RESET VECTOR		:
;----------------------------------------
VECTOR	SEGMENT AT 0FFFFH

;----- POWER ON RESET

	JMP	RESET

	DB	'11/08/82'              ; RELEASE MARKER
VECTOR	ENDS
	END
